From fa4c0383d9b08f5cd4c6b56861745d51b42e72fc Mon Sep 17 00:00:00 2001 From: Michael Kurz Date: Wed, 16 Feb 2022 21:46:51 +0100 Subject: [PATCH] #38: start adding data fetching for stakeholders and commits --- .../team-awareness/chart/chart.js | 16 +++--- .../team-awareness/chart/index.js | 18 ++++++- .../visualizations/team-awareness/config.js | 50 +++++++++++++++---- ui/src/visualizations/team-awareness/help.js | 15 +++--- ui/src/visualizations/team-awareness/index.js | 28 +++++++---- .../team-awareness/reducers/config.js | 10 ++++ .../team-awareness/reducers/data.js | 24 +++++++++ .../team-awareness/reducers/index.js | 6 ++- .../team-awareness/sagas/getCommits.js | 33 ++++++++++++ .../team-awareness/sagas/getStakeholders.js | 34 +++++++++++++ .../team-awareness/sagas/index.js | 47 +++++++++++++++-- .../team-awareness/util/util.js | 7 +++ 12 files changed, 247 insertions(+), 41 deletions(-) create mode 100644 ui/src/visualizations/team-awareness/reducers/config.js create mode 100644 ui/src/visualizations/team-awareness/reducers/data.js create mode 100644 ui/src/visualizations/team-awareness/sagas/getCommits.js create mode 100644 ui/src/visualizations/team-awareness/sagas/getStakeholders.js create mode 100644 ui/src/visualizations/team-awareness/util/util.js diff --git a/ui/src/visualizations/team-awareness/chart/chart.js b/ui/src/visualizations/team-awareness/chart/chart.js index 1f679ce9..617ec48e 100644 --- a/ui/src/visualizations/team-awareness/chart/chart.js +++ b/ui/src/visualizations/team-awareness/chart/chart.js @@ -3,11 +3,13 @@ import React from 'react'; export default class TeamArwareness extends React.Component { - constructor(props) { - super(props); - } + constructor(props) { + super(props); + } - render() { - return (

Hello World

); - } -} \ No newline at end of file + render() { + console.log('Chraph state'); + console.log(this.props); + return

Hello World

; + } +} diff --git a/ui/src/visualizations/team-awareness/chart/index.js b/ui/src/visualizations/team-awareness/chart/index.js index 2ed5ff13..fa3f8f70 100644 --- a/ui/src/visualizations/team-awareness/chart/index.js +++ b/ui/src/visualizations/team-awareness/chart/index.js @@ -1,5 +1,19 @@ 'use strict'; -import chart from './chart.js'; +import { connect } from 'react-redux'; +import { getState } from '../util/util.js'; +import Chart from './chart.js'; -export default chart; \ No newline at end of file +const mapStateToProps = (appState /*, chartState */) => { + const vizState = getState(appState); + return { + data: { + stakeholders: vizState.data.data.stakeholders, + activity: vizState.data.data.activity + } + }; +}; + +const mapDispatchToProps = () => ({}); + +export default connect(mapStateToProps, mapDispatchToProps)(Chart); diff --git a/ui/src/visualizations/team-awareness/config.js b/ui/src/visualizations/team-awareness/config.js index 8425b919..e1e49b55 100644 --- a/ui/src/visualizations/team-awareness/config.js +++ b/ui/src/visualizations/team-awareness/config.js @@ -1,13 +1,45 @@ -import React from "react" +'use strict'; +import React from 'react'; +import { connect } from 'react-redux'; +import { setSelectActivity } from './sagas'; - -export default class ConfigComponent extends React.Component { - constructor(props) { - super(props); +const mapStateToProps = (appState /*, ownProps*/) => { + const awarenessState = appState.visualizations.teamAwareness.state.config; + return { + config: { + selectedActivity: awarenessState.selectedActivity } + }; +}; +const mapDispatchToProps = dispatch => { + return { + onSelectActivity: selectActivity => dispatch(setSelectActivity(selectActivity)) + }; +}; - render() { - return (

Team Arwareness Config

); - } -} \ No newline at end of file +class ConfigComponent extends React.Component { + constructor(props) { + super(props); + } + + render() { + return ( +
+
+
Activity:
+
+ +
+
+
+ ); + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(ConfigComponent); diff --git a/ui/src/visualizations/team-awareness/help.js b/ui/src/visualizations/team-awareness/help.js index f7b40fed..50b6f022 100644 --- a/ui/src/visualizations/team-awareness/help.js +++ b/ui/src/visualizations/team-awareness/help.js @@ -2,15 +2,12 @@ import React from 'react'; - export default class HelpComponent extends React.Component { - constructor(props) { - super(props); - } + constructor(props) { + super(props); + } - render() { - return ( -

Team Arwareness Help 1

- ); - } + render() { + return

Team Awareness Help 1

; + } } diff --git a/ui/src/visualizations/team-awareness/index.js b/ui/src/visualizations/team-awareness/index.js index 2894659b..5eeb83eb 100644 --- a/ui/src/visualizations/team-awareness/index.js +++ b/ui/src/visualizations/team-awareness/index.js @@ -1,17 +1,27 @@ 'use strict'; -import ChartComponent from './chart'; +import TeamAwarenessComponent from './chart'; import ConfigComponent from './config'; import HelpComponent from './help'; import saga from './sagas'; import reducer from './reducers'; +import { connect } from 'react-redux'; + +const mapStateToProps = () => { + return {}; +}; +const mapDispatchToProps = () => { + return {}; +}; + +const ChartComponent = connect(mapStateToProps, mapDispatchToProps)(TeamAwarenessComponent); export default { - id: "teamArwareness", - label: "Team Arwareness", - saga, - reducer, - ChartComponent, - ConfigComponent, - HelpComponent -}; \ No newline at end of file + id: 'teamAwareness', + label: 'Team Awareness', + saga, + reducer, + ChartComponent, + ConfigComponent, + HelpComponent +}; diff --git a/ui/src/visualizations/team-awareness/reducers/config.js b/ui/src/visualizations/team-awareness/reducers/config.js new file mode 100644 index 00000000..ef817d53 --- /dev/null +++ b/ui/src/visualizations/team-awareness/reducers/config.js @@ -0,0 +1,10 @@ +'use strict'; + +import { handleActions } from 'redux-actions'; + +export default handleActions( + {}, + { + selectedActivity: 'commits' + } +); diff --git a/ui/src/visualizations/team-awareness/reducers/data.js b/ui/src/visualizations/team-awareness/reducers/data.js new file mode 100644 index 00000000..518e098a --- /dev/null +++ b/ui/src/visualizations/team-awareness/reducers/data.js @@ -0,0 +1,24 @@ +'use strict'; + +import _ from 'lodash'; +import { handleActions } from 'redux-actions'; + +export default handleActions( + { + REQUEST_TEAM_AWARENESS_DATA: state => { + return _.assign({}, state, { isFetching: true }); + }, + RECEIVE_TEAM_AWARENESS_DATA: (state, action) => { + return _.assign({}, state, { + data: action.payload, + isFetching: false, + receivedAt: action.meta.receivedAt + }); + } + }, + { + data: {}, + lastFetched: null, + isFetching: null + } +); diff --git a/ui/src/visualizations/team-awareness/reducers/index.js b/ui/src/visualizations/team-awareness/reducers/index.js index 264a29ad..738455a4 100644 --- a/ui/src/visualizations/team-awareness/reducers/index.js +++ b/ui/src/visualizations/team-awareness/reducers/index.js @@ -1,5 +1,7 @@ 'use strict'; -import { handleActions } from 'redux-actions'; +import { combineReducers } from 'redux'; +import data from './data'; +import config from './config'; -export default handleActions({},{}); \ No newline at end of file +export default combineReducers({ config, data }); diff --git a/ui/src/visualizations/team-awareness/sagas/getCommits.js b/ui/src/visualizations/team-awareness/sagas/getCommits.js new file mode 100644 index 00000000..e6e31f5c --- /dev/null +++ b/ui/src/visualizations/team-awareness/sagas/getCommits.js @@ -0,0 +1,33 @@ +import { graphQl, traversePages } from '../../../utils'; + +/** + * Fetches commit data via GraphQL API from the data source. + */ +export default () => { + const buildList = []; + return traversePages(getCommits, build => buildList.push(build)).then(() => buildList); +}; + +const getCommits = (page, perPage) => { + return graphQl + .query( + ` + query($page:Int, $perPage:Int) { + commits(page:$page, perPage:$perPage) { + data { + date + stakeholder { + id + } + stats { + additions + deletions + } + } + } + }`, + page, + perPage + ) + .then(result => result.commits); +}; diff --git a/ui/src/visualizations/team-awareness/sagas/getStakeholders.js b/ui/src/visualizations/team-awareness/sagas/getStakeholders.js new file mode 100644 index 00000000..5c0eec35 --- /dev/null +++ b/ui/src/visualizations/team-awareness/sagas/getStakeholders.js @@ -0,0 +1,34 @@ +import { graphQl, traversePages } from '../../../utils'; + +/** + * Fetches stakeholder data via GraphQL API from the data source. + */ +export default () => { + const buildList = []; + return traversePages(getStakeholders, build => buildList.push(build)).then(() => buildList); +}; + +/** + * Fetches paginated stakeholder data via GraphQL API from the data source. + * + * @param {Number} page The page number to fetch. + * @param {Number} perPage The number of items per page. + * @returns {[]} An array of stakeholder data. + */ +const getStakeholders = (page, perPage) => { + return graphQl + .query( + ` + query($page:Int, $perPage:Int){ + stakeholders(page:$page, perPage: $perPage) { + data { + id + gitSignature + } + } + }`, + page, + perPage + ) + .then(result => result.stakeholders); +}; diff --git a/ui/src/visualizations/team-awareness/sagas/index.js b/ui/src/visualizations/team-awareness/sagas/index.js index 69b1a189..ac979f07 100644 --- a/ui/src/visualizations/team-awareness/sagas/index.js +++ b/ui/src/visualizations/team-awareness/sagas/index.js @@ -1,8 +1,49 @@ 'use strict'; -import { put } from "redux-saga/effects"; +import { createAction } from 'redux-actions'; +import { fork, takeEvery, throttle } from 'redux-saga/effects'; +import { fetchFactory, mapSaga, timestampedActionFactory } from '../../../sagas/utils'; +import getCommits from './getCommits'; +import getStakeholders from './getStakeholders'; +export const setSelectActivity = createAction('SET_SELECT_ACTIVITY'); + +export const requestTeamAwarenessData = createAction('REQUEST_TEAM_AWARENESS_DATA'); +export const receiveTeamAwarenessData = timestampedActionFactory('RECEIVE_TEAM_AWARENESS_DATA'); +export const receiveTeamAwarenessDataError = timestampedActionFactory('RECEIVE_TEAM_AWARENESS_DATA'); + +export const requestRefresh = createAction('REQUEST_REFRESH'); +const refresh = createAction('REFRESH'); export default function*() { - yield put(); -}; \ No newline at end of file + yield* fetchAwarenessData(); + + yield fork(watchRefreshRequests); + yield fork(watchMessages); + yield fork(watchRefresh); +} + +function* watchRefreshRequests() { + yield throttle(2000, 'REQUEST_REFRESH', mapSaga(refresh)); +} +function* watchRefresh() { + yield takeEvery('REFRESH', fetchAwarenessData); +} +function* watchMessages() { + yield takeEvery('message', mapSaga(requestRefresh)); +} + +export const fetchAwarenessData = fetchFactory( + function*() { + //const state = getState(yield select()); + return yield Promise.all([getStakeholders(), getCommits()]).then(result => { + return { + stakeholders: result[0], + activity: result[1] + }; + }); + }, + requestTeamAwarenessData, + receiveTeamAwarenessData, + receiveTeamAwarenessDataError +); diff --git a/ui/src/visualizations/team-awareness/util/util.js b/ui/src/visualizations/team-awareness/util/util.js new file mode 100644 index 00000000..d66927d7 --- /dev/null +++ b/ui/src/visualizations/team-awareness/util/util.js @@ -0,0 +1,7 @@ +/** + * Returns current state of the visualization. + * + * @param {any} appState State of the application. + * @returns State of Team Awareness Visualization. + */ +export const getState = appState => appState.visualizations.teamAwareness.state;