From aceb7cf635157eb0927c2dba9fffce308e0a6d45 Mon Sep 17 00:00:00 2001 From: Ayush Kumar Sinha Date: Thu, 15 Aug 2019 21:15:23 +0530 Subject: [PATCH] Testman integration (#4) --- app.js | 40 ++++++++++++++++---- client/src/components/Commits.js | 12 ++++-- client/src/components/CommitsCard.js | 12 +++++- client/src/components/Pulls.js | 12 ++++-- client/src/components/PullsCard.js | 13 ++++++- client/src/components/TestDetails.js | 36 ++++++++++++++++++ client/src/redux/actions/index.js | 22 ++++++++++- client/src/redux/api/index.js | 21 ++++++++++ client/src/redux/constants/index.js | 6 +++ client/src/redux/reducers/index.js | 4 +- client/src/redux/reducers/testmanReducer.js | 20 ++++++++++ client/src/redux/sagas/commitsSaga.js | 32 ++++++++-------- client/src/redux/sagas/commitsTestmanSaga.js | 25 ++++++++++++ client/src/redux/sagas/index.js | 7 +++- client/src/redux/sagas/pullsBuildSaga.js | 8 ++-- client/src/redux/sagas/pullsTestmanSaga.js | 25 ++++++++++++ package-lock.json | 19 ++++++++-- package.json | 3 +- 18 files changed, 276 insertions(+), 41 deletions(-) create mode 100644 client/src/components/TestDetails.js create mode 100644 client/src/redux/reducers/testmanReducer.js create mode 100644 client/src/redux/sagas/commitsTestmanSaga.js create mode 100644 client/src/redux/sagas/pullsTestmanSaga.js diff --git a/app.js b/app.js index 173f707..92d7329 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,7 @@ const dotenv = require('dotenv'); const express = require('express'); const rp = require('request-promise'); +const convert = require('xml-js'); const app = express(); //app.disable('query parser'); dotenv.config(); @@ -82,7 +83,7 @@ app.get('/api/branches', (req, res) => { res.json(body); }) .catch(function(err) { - res.json({ error: 'oops...something went wrong' }); + res.json({ error: 'oops...something went wrong' + err }); }); }); @@ -122,7 +123,7 @@ app.get('/api/pulls', (req, res) => { res.json(dataAndPage); }) .catch(function(err) { - res.json({ error: 'oops...something went wrong' }); + res.json({ error: 'oops...something went wrong' + err }); }); }); @@ -152,7 +153,7 @@ app.get('/api/buildsets', (req, res) => { res.json(body); }) .catch(function(err) { - res.json({ error: 'oops...something went wrong' }); + res.json({ error: 'oops...something went wrong' + err }); }); }); @@ -179,7 +180,7 @@ app.get('/api/buildreq', (req, res) => { res.json(body); }) .catch(function(err) { - res.json({ error: 'oops...something went wrong' }); + res.json({ error: 'oops...something went wrong' + err }); }); }); @@ -206,11 +207,11 @@ app.get('/api/builds', (req, res) => { res.json(body); }) .catch(function(err) { - res.json({ error: 'oops...something went wrong' }); + res.json({ error: 'oops...something went wrong' + err }); }); }); -//------- BRANCHES END-POINT ------- +//------- BUILDERS END-POINT ------- function builderReq() { const builders = { @@ -231,7 +232,32 @@ app.get('/api/builders', (req, res) => { res.json(body); }) .catch(function(err) { - res.json({ error: 'oops...something went wrong' }); + res.json({ error: 'oops...something went wrong' + err }); + }); +}); + +//------- TESTMAN END-POINT ------- + +function testReq(startrev, endrev, page) { + const tests = { + uri: `https://reactos.org/testman/ajax-search.php?startrev=${startrev}&endrev=${endrev}&page=${page}&resultlist=0&requesttype=2`, + resolveWithFullResponse: false, + headers: { + 'User-Agent': 'Request-Promise' + } + }; + + return tests; +} + +app.get('/api/testman', (req, res) => { + rp(testReq(req.query.startrev, req.query.endrev, req.query.page)) + .then(body => { + const result = convert.xml2json(body, { compact: true, spaces: 2 }); + res.send(result); + }) + .catch(function(err) { + res.json({ error: 'oops...something went wrong' + err }); }); }); diff --git a/client/src/components/Commits.js b/client/src/components/Commits.js index 64c6706..f278165 100644 --- a/client/src/components/Commits.js +++ b/client/src/components/Commits.js @@ -14,7 +14,11 @@ class Commits extends React.Component { renderCommits = commit => { return (
- +
); }; @@ -86,7 +90,8 @@ const mapStateToProps = ({ error, branch, page, - build + build, + testData }) => ({ isLoading, commits, @@ -94,7 +99,8 @@ const mapStateToProps = ({ error, branch, page, - build + build, + testData }); const mapDispatchToProps = dispatch => ({ diff --git a/client/src/components/CommitsCard.js b/client/src/components/CommitsCard.js index d59e4e9..35fe93f 100644 --- a/client/src/components/CommitsCard.js +++ b/client/src/components/CommitsCard.js @@ -1,6 +1,7 @@ import React from 'react'; import { UncontrolledCollapse, CardBody, Card, CardHeader } from 'reactstrap'; import BuildDetails from './BuildDetails'; +import TestDetails from './TestDetails'; function CommitsCard(props) { let tog = 'toggler' + props.sha; @@ -87,7 +88,16 @@ function CommitsCard(props) { ) : (
- Loading Build Data ... + Loading Builds... +
+ )} +
+
Test Details:
+ {props.tests ? ( + + ) : ( +
+ No data Exists
)} diff --git a/client/src/components/Pulls.js b/client/src/components/Pulls.js index 0f78fb2..7bc8d39 100644 --- a/client/src/components/Pulls.js +++ b/client/src/components/Pulls.js @@ -13,7 +13,11 @@ class Pulls extends React.Component { renderPulls = pull => { return (
- +
); }; @@ -80,14 +84,16 @@ const mapStateToProps = ({ page, isLoading, error, - build + build, + testData }) => ({ pulls, builders, page, isLoading, error, - build + build, + testData }); const mapDispatchToProps = dispatch => ({ diff --git a/client/src/components/PullsCard.js b/client/src/components/PullsCard.js index b487119..9fdc377 100644 --- a/client/src/components/PullsCard.js +++ b/client/src/components/PullsCard.js @@ -1,6 +1,7 @@ import React from 'react'; import { UncontrolledCollapse, CardBody, Card, CardHeader } from 'reactstrap'; import BuildDetails from './BuildDetails'; +import TestDetails from './TestDetails'; function PullsCard(props) { let tog = 'toggler' + props.id; @@ -12,6 +13,7 @@ function PullsCard(props) {
{props.number}
+
{props.merge_commit_sha.substring(0, 7)}
{props.state}
{props.user.login}
@@ -49,7 +51,16 @@ function PullsCard(props) { {props.builds ? ( ) : ( -
Loading Pulls data...
+
Loading Builds...
+ )} +
+
Test Details:
+ {props.tests ? ( + + ) : ( +

+ No data Exists +

)} diff --git a/client/src/components/TestDetails.js b/client/src/components/TestDetails.js new file mode 100644 index 0000000..414b2be --- /dev/null +++ b/client/src/components/TestDetails.js @@ -0,0 +1,36 @@ +import React from 'react'; + +function renderTest(test) { + return ( + +
Test id: {test.id}
+ +
Count: {test.count}
+
Failures: {test.failures}
+
+ ); +} + +function TestDetails(props) { + return ( + + {props.tests.length > 0 ? ( +
{props.tests.map(renderTest)}
+ ) : ( +

+ No data Exists +

+ )} +
+ ); +} + +export default TestDetails; diff --git a/client/src/redux/actions/index.js b/client/src/redux/actions/index.js index 2a30890..942474b 100644 --- a/client/src/redux/actions/index.js +++ b/client/src/redux/actions/index.js @@ -1,4 +1,11 @@ -import { COMMITS, BRANCHES, PULLS, BUILD_DATA, BUILDERS } from '../constants'; +import { + COMMITS, + BRANCHES, + PULLS, + BUILD_DATA, + BUILDERS, + TESTMAN_DATA +} from '../constants'; export const loadCommits = newPage => ({ type: COMMITS.LOAD, @@ -28,6 +35,19 @@ export const setBuildSetsError = error => ({ error }); +export const loadTestman = () => ({ + type: TESTMAN_DATA.LOAD +}); +export const setTestman = tests => ({ + type: TESTMAN_DATA.LOAD_SUCCESS, + tests +}); + +export const setTestmanError = error => ({ + type: TESTMAN_DATA.LOAD_FAIL, + error +}); + export const setPages = (next, prev) => ({ type: 'PAGE_LOAD_SUCCESS', next, diff --git a/client/src/redux/api/index.js b/client/src/redux/api/index.js index 59db260..cc0b69c 100644 --- a/client/src/redux/api/index.js +++ b/client/src/redux/api/index.js @@ -7,6 +7,27 @@ export const fetchCommits = async (sha, page) => { return data; }; +export const fetchTests = async (startrev, endrev, page) => { + let results = []; + let keepGoing = true; + while (keepGoing) { + const response = await fetch( + `/api/testman?startrev=${startrev}&endrev=${endrev}&page=${page}` + ); + let data = await response.json(); + results.push(data.results.result); + if (parseInt(data.results.resultcount._text) > 10) { + page++; + } else { + keepGoing = false; + return results.flat(); + } + if (response.status >= 400) { + throw new Error(data.errors); + } + } +}; + export const fetchBuildSets = async str => { if (str) { const response = await fetch(`/api/buildsets?${str}`); diff --git a/client/src/redux/constants/index.js b/client/src/redux/constants/index.js index ee9db80..bca7847 100644 --- a/client/src/redux/constants/index.js +++ b/client/src/redux/constants/index.js @@ -24,6 +24,12 @@ export const BUILD_DATA = { LOAD_FAIL: 'BUILD_DATA_LOAD_FAIL' }; +export const TESTMAN_DATA = { + LOAD: 'TESTMAN_DATA_LOAD', + LOAD_SUCCESS: 'TESTMAN_DATA_LOAD_SUCCESS', + LOAD_FAIL: 'TESTMAN_DATA_LOAD_FAIL' +}; + export const BUILDERS = { LOAD: 'BUILDER_LOAD', LOAD_SUCCESS: 'BUILDER_LOAD_SUCCESS', diff --git a/client/src/redux/reducers/index.js b/client/src/redux/reducers/index.js index cef9eea..a14b594 100644 --- a/client/src/redux/reducers/index.js +++ b/client/src/redux/reducers/index.js @@ -10,6 +10,7 @@ import pullsReducer from './pullsReducer'; import pageReducer from './pageReducer'; import pullStateReducer from './pullStateReducer'; import buildStatusReducer from './buildStatusReducer'; +import testmanReducer from './testmanReducer'; const rootReducer = combineReducers({ isLoading: loadingReducer, @@ -21,7 +22,8 @@ const rootReducer = combineReducers({ pulls: pullsReducer, pullState: pullStateReducer, page: pageReducer, - build: buildStatusReducer + build: buildStatusReducer, + testData: testmanReducer }); export default rootReducer; diff --git a/client/src/redux/reducers/testmanReducer.js b/client/src/redux/reducers/testmanReducer.js new file mode 100644 index 0000000..f6c2512 --- /dev/null +++ b/client/src/redux/reducers/testmanReducer.js @@ -0,0 +1,20 @@ +import { TESTMAN_DATA } from '../constants'; +const testmanReducer = (state = {}, action) => { + if (action.type === TESTMAN_DATA.LOAD_SUCCESS) { + let cleanedTestmanData = {}; + for (const [sha, tests] of Object.entries(action.tests)) { + cleanedTestmanData[sha] = tests.map(t => { + let reducedKeyVal = {}; + for (const [k, v] of Object.entries(t)) { + reducedKeyVal[k] = v._text; + } + return reducedKeyVal; + }) + } + return cleanedTestmanData; + } + + return state; +}; + +export default testmanReducer; diff --git a/client/src/redux/sagas/commitsSaga.js b/client/src/redux/sagas/commitsSaga.js index 8098882..1a3032c 100644 --- a/client/src/redux/sagas/commitsSaga.js +++ b/client/src/redux/sagas/commitsSaga.js @@ -6,23 +6,23 @@ export const getBranch = state => state.branch; export const getNewPage = state => parseInt(state.isLoading.newPage, 10); function* handleCommitsLoad() { - try { - const branch = yield select(getBranch); - const newPage = yield select(getNewPage); - let commits = yield call(fetchCommits, branch, newPage); - yield put(setCommits(commits.commits.body)); - yield put( - setPages( - commits.page.next !== undefined ? commits.page.next.page : null, - commits.page.prev !== undefined ? commits.page.prev.page : null - ) - ); - } catch (error) { - //dispatch error - yield put(setCommitsError(error.toString())); - } + try { + const branch = yield select(getBranch); + const newPage = yield select(getNewPage); + let commits = yield call(fetchCommits, branch, newPage); + yield put(setCommits(commits.commits.body)); + yield put( + setPages( + commits.page.next !== undefined ? commits.page.next.page : null, + commits.page.prev !== undefined ? commits.page.prev.page : null + ) + ); + } catch (error) { + //dispatch error + yield put(setCommitsError(error.toString())); + } } export default function* watchCommitsLoad() { - yield throttle(500, COMMITS.LOAD, handleCommitsLoad); + yield throttle(500, COMMITS.LOAD, handleCommitsLoad); } diff --git a/client/src/redux/sagas/commitsTestmanSaga.js b/client/src/redux/sagas/commitsTestmanSaga.js new file mode 100644 index 0000000..ab4ec51 --- /dev/null +++ b/client/src/redux/sagas/commitsTestmanSaga.js @@ -0,0 +1,25 @@ +import { takeEvery, call, select, put } from 'redux-saga/effects'; +import { COMMITS } from '../constants'; +import { fetchTests } from '../api'; +import { setTestman, setTestmanError } from '../actions'; + +function* handleTestmanLoad() { + try { + const commits = yield select(state => state.commits); + const shas = commits.map(commit => commit.sha); + const testResults = yield call(fetchTests, shas[9], shas[0], 1); + const testBySha = {}; + for (let sha of shas) { + testBySha[sha] = testResults.filter(test => + sha.startsWith(test.revision._text) + ); + } + yield put(setTestman(testBySha)); + } catch (error) { + yield put(setTestmanError(error.toString())); + } +} + +export default function* watchTestmanLoad() { + yield takeEvery(COMMITS.LOAD_SUCCESS, handleTestmanLoad); +} diff --git a/client/src/redux/sagas/index.js b/client/src/redux/sagas/index.js index 9fa47c3..d92ab2a 100644 --- a/client/src/redux/sagas/index.js +++ b/client/src/redux/sagas/index.js @@ -6,6 +6,9 @@ import pullsSaga from './pullsSaga'; import buildersSaga from './buildersSaga'; import buildSetSaga from './buildSetSaga'; import pullsBuildSaga from './pullsBuildSaga'; +import commitsTestmanSaga from './commitsTestmanSaga'; +import pullsTestmanSaga from './pullsTestmanSaga'; + export default function* rootSaga() { yield all([ commitsSaga(), @@ -13,6 +16,8 @@ export default function* rootSaga() { pullsSaga(), buildersSaga(), buildSetSaga(), - pullsBuildSaga() + pullsBuildSaga(), + commitsTestmanSaga(), + pullsTestmanSaga() ]); } diff --git a/client/src/redux/sagas/pullsBuildSaga.js b/client/src/redux/sagas/pullsBuildSaga.js index 5045cc7..ed5abbe 100644 --- a/client/src/redux/sagas/pullsBuildSaga.js +++ b/client/src/redux/sagas/pullsBuildSaga.js @@ -21,7 +21,8 @@ function getBuildReqQString(pulls, buildSetsRaw) { buildSetsRaw.filter( build => build.sourcestamps[0].branch === `refs/pull/${pull.number}/head` || - build.sourcestamps[0].branch === `refs/pull/${pull.number}/merge` + build.sourcestamps[0].branch === `refs/pull/${pull.number}/merge` || + build.sourcestamps[0].revision === `${pull.merge_commit_sha}` ) ) .map(bd => 'buildsetid__contains=' + bd.bsid) @@ -49,12 +50,13 @@ function* handlePullsBuildLoad() { const buildsRaw = yield call(fetchBuilds, getBuildQString(buildReqsRaw)); const buildsByPR = {}; - for (let { number } of pulls) { + for (let { number, merge_commit_sha } of pulls) { const buildSetIds = buildSetsRaw .filter( bs => bs.sourcestamps[0].branch === `refs/pull/${number}/head` || - bs.sourcestamps[0].branch === `refs/pull/${number}/merge` + bs.sourcestamps[0].branch === `refs/pull/${number}/merge` || + bs.sourcestamps[0].revision === `${merge_commit_sha}` ) .map(bs => bs.bsid); const buildReqIds = buildReqsRaw diff --git a/client/src/redux/sagas/pullsTestmanSaga.js b/client/src/redux/sagas/pullsTestmanSaga.js new file mode 100644 index 0000000..d2018d0 --- /dev/null +++ b/client/src/redux/sagas/pullsTestmanSaga.js @@ -0,0 +1,25 @@ +import { takeEvery, call, select, put } from 'redux-saga/effects'; +import { PULLS } from '../constants'; +import { fetchTests } from '../api'; +import { setTestman, setTestmanError } from '../actions'; + +function* handleTestmanLoad() { + try { + const pulls = yield select(state => state.pulls); + const shas = pulls.map(pull => pull.merge_commit_sha); + const testResults = yield call(fetchTests, shas[9], shas[0], 1); + const testByPulls = {}; + for (let sha of shas) { + testByPulls[sha] = testResults.filter(test => + sha.startsWith(test.revision._text) + ); + } + yield put(setTestman(testByPulls)); + } catch (error) { + yield put(setTestmanError(error.toString())); + } +} + +export default function* watchTestmanLoad() { + yield takeEvery(PULLS.LOAD_SUCCESS, handleTestmanLoad); +} diff --git a/package-lock.json b/package-lock.json index f5531eb..60263dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1262,9 +1262,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "loose-envify": { "version": "1.4.0", @@ -1909,6 +1909,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", @@ -2363,6 +2368,14 @@ "mkdirp": "^0.5.1" } }, + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index d25b296..22e5baa 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "path": "^0.12.7", "react-router-dom": "^5.0.1", "request": "^2.88.0", - "request-promise": "^4.2.4" + "request-promise": "^4.2.4", + "xml-js": "^1.6.11" } }