mirror of
https://github.com/reactos/developer-web-interface.git
synced 2024-11-23 03:49:43 +00:00
Fixing buildset req (#3)
* Adding builders to state * indentation fix * printing buildData in commitsCard.js * changing into functional component and mapping stateToProps * buildset endpoint now loads only relevent data * fixing reducer- builders were undefined * Adding builds to PR tab * increasing the range of unixF * adding message to outer check
This commit is contained in:
parent
3ff7737442
commit
842c5acd06
13
app.js
13
app.js
@ -128,10 +128,10 @@ app.get('/api/pulls', (req, res) => {
|
||||
|
||||
//------- BUILD-SET END-POINT -------
|
||||
|
||||
function buildSetReq() {
|
||||
function buildSetReq(str) {
|
||||
//https://build.reactos.org/api/v2/buildsets?field=bsid&field=sourcestamps&order=-bsid&offset=0&limit=200
|
||||
const buildSets = {
|
||||
uri:
|
||||
'https://build.reactos.org/api/v2/buildsets?field=bsid&field=sourcestamps&order=-bsid&offset=0&limit=200',
|
||||
uri: `https://build.reactos.org/api/v2/buildsets?field=bsid&field=sourcestamps&field=submitted_at&order=-bsid${str}`,
|
||||
headers: {
|
||||
'User-Agent': 'Request-Promise'
|
||||
},
|
||||
@ -142,7 +142,12 @@ function buildSetReq() {
|
||||
}
|
||||
|
||||
app.get('/api/buildsets', (req, res) => {
|
||||
rp(buildSetReq())
|
||||
let q =
|
||||
'&submitted_at__le=' +
|
||||
req.query.submitted_at__le +
|
||||
'&submitted_at__ge=' +
|
||||
req.query.submitted_at__ge;
|
||||
rp(buildSetReq(q))
|
||||
.then(body => {
|
||||
res.json(body);
|
||||
})
|
||||
|
@ -1,53 +1,61 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
function Build({builderid, number, builderName, started_at, complete_at, state_string}) {
|
||||
return (<React.Fragment>
|
||||
<div className='col-sm-3'>
|
||||
<a
|
||||
target='_blank'
|
||||
rel='noreferrer noopener'
|
||||
href={`https://build.reactos.org/#builders/${
|
||||
builderid
|
||||
}/builds/${number}`}
|
||||
>
|
||||
{builderName}
|
||||
</a>
|
||||
</div>
|
||||
<div className='col-sm-3'>
|
||||
{state_string}
|
||||
{state_string === 'build successful' ? (
|
||||
<i className='fa fa-check' />
|
||||
) : (
|
||||
<i />
|
||||
)}
|
||||
</div>
|
||||
<div className='col-sm-3'>started_at:{started_at}</div>
|
||||
<div className='col-sm-3'>complete_at:{complete_at}</div>
|
||||
</React.Fragment>);
|
||||
function Build({ builderName, ...build }) {
|
||||
let completedDate = new Date(build.complete_at * 1000);
|
||||
let startedDate = new Date(build.started_at * 1000);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className='col-sm-2'>
|
||||
<a
|
||||
target='_blank'
|
||||
rel='noreferrer noopener'
|
||||
href={`https://build.reactos.org/#builders/${
|
||||
build.builderid
|
||||
}/builds/${build.number}`}
|
||||
>
|
||||
{builderName}
|
||||
</a>
|
||||
</div>
|
||||
<div className='col-sm-3'>
|
||||
{build.state_string}
|
||||
{build.state_string === 'build successful' ? (
|
||||
<i className='fa fa-check' />
|
||||
) : (
|
||||
<i />
|
||||
)}
|
||||
</div>
|
||||
<div className='col-sm-3'>Started: {startedDate.toLocaleString()}</div>
|
||||
<div className='col-sm-4'>
|
||||
Completed: {build.complete_at ? completedDate.toLocaleString() : <p />}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function renderBuild(props) {
|
||||
return <Build key={props.buildid} {...props} />;
|
||||
}
|
||||
|
||||
function BuildDetails({builds}) {
|
||||
return (<React.Fragment>
|
||||
{builds.length > 0 ? (
|
||||
<div className='row'>{builds.map(renderBuild)}</div>
|
||||
) : (
|
||||
<p>
|
||||
<strong>No data Exists</strong>
|
||||
</p>
|
||||
)}
|
||||
</React.Fragment>);
|
||||
function BuildDetails({ builds }) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{builds.length > 0 ? (
|
||||
<div className='row'>{builds.map(renderBuild)}</div>
|
||||
) : (
|
||||
<p>
|
||||
<strong>No data Exists</strong>
|
||||
</p>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = ({ builders }, ownProps) => {
|
||||
return {
|
||||
builds: ownProps.builds.map(
|
||||
build => {...build, builderName: builders[build.builderid].name}
|
||||
)
|
||||
builds: ownProps.builds.map(build => ({
|
||||
...build,
|
||||
builderName: builders[build.builderid].name
|
||||
}))
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -83,7 +83,13 @@ function CommitsCard(props) {
|
||||
</div>
|
||||
<hr />
|
||||
<h5>Build Details:</h5>
|
||||
<BuildDetails builds={props.builds} />
|
||||
{props.builds ? (
|
||||
<BuildDetails builds={props.builds} />
|
||||
) : (
|
||||
<div>
|
||||
<strong>Loading Build Data ...</strong>
|
||||
</div>
|
||||
)}
|
||||
</CardBody>
|
||||
</UncontrolledCollapse>
|
||||
</Card>
|
||||
|
@ -7,80 +7,93 @@ import Loading from './Loading';
|
||||
import PullsCard from './PullsCard';
|
||||
|
||||
class Pulls extends React.Component {
|
||||
componentDidMount() {
|
||||
this.props.loadPulls();
|
||||
}
|
||||
renderPulls = pull => {
|
||||
return (
|
||||
<div className='panel-margin' key={pull.id}>
|
||||
<PullsCard {...pull} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div className='container margin'>
|
||||
<h2>Latest Pulls</h2>
|
||||
<PullState />
|
||||
{this.props.isLoading.load ? (
|
||||
<Loading text='Fetching latest PRs for you...' />
|
||||
) : (
|
||||
<div>
|
||||
<div>{this.props.pulls.map(this.renderPulls)}</div>
|
||||
{this.props.error ? (
|
||||
<div className='error'>
|
||||
Unexpected Error occured. Kindly Reload the page
|
||||
<br />
|
||||
Err:{this.props.error}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => {
|
||||
this.props.loadPulls(this.props.page.prev);
|
||||
}}
|
||||
className='btn btn-primary '
|
||||
disabled={this.props.page.prev === null || this.props.error !== null}
|
||||
>
|
||||
<i className='fa fa-caret-left' aria-hidden='true' />
|
||||
Previous Page{' '}
|
||||
</button>{' '}
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => {
|
||||
this.props.loadPulls(this.props.page.next);
|
||||
}}
|
||||
className='btn btn-primary'
|
||||
disabled={this.props.page.next === null || this.props.error !== null}
|
||||
>
|
||||
Next Page{' '}
|
||||
<i className='fa fa-caret-right' aria-hidden='true' />
|
||||
</button>
|
||||
<footer className='blockquote-footer'>
|
||||
Page {this.props.page.next - 1}
|
||||
</footer>
|
||||
<div className='footer-blockquote' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
componentDidMount() {
|
||||
this.props.loadPulls();
|
||||
}
|
||||
renderPulls = pull => {
|
||||
return (
|
||||
<div className='panel-margin' key={pull.id}>
|
||||
<PullsCard {...pull} builds={this.props.build[pull.number]} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div className='container margin'>
|
||||
<h2>Latest Pulls</h2>
|
||||
<PullState />
|
||||
{this.props.isLoading.load ? (
|
||||
<Loading text='Fetching latest PRs for you...' />
|
||||
) : (
|
||||
<div>
|
||||
<div>{this.props.pulls.map(this.renderPulls)}</div>
|
||||
{this.props.error ? (
|
||||
<div className='error'>
|
||||
Unexpected Error occured. Kindly Reload the page
|
||||
<br />
|
||||
Err:{this.props.error}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => {
|
||||
this.props.loadPulls(this.props.page.prev);
|
||||
}}
|
||||
className='btn btn-primary '
|
||||
disabled={
|
||||
this.props.page.prev === null || this.props.error !== null
|
||||
}
|
||||
>
|
||||
<i className='fa fa-caret-left' aria-hidden='true' />
|
||||
Previous Page{' '}
|
||||
</button>{' '}
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => {
|
||||
this.props.loadPulls(this.props.page.next);
|
||||
}}
|
||||
className='btn btn-primary'
|
||||
disabled={
|
||||
this.props.page.next === null || this.props.error !== null
|
||||
}
|
||||
>
|
||||
Next Page{' '}
|
||||
<i className='fa fa-caret-right' aria-hidden='true' />
|
||||
</button>
|
||||
<footer className='blockquote-footer'>
|
||||
Page {this.props.page.next - 1}
|
||||
</footer>
|
||||
<div className='footer-blockquote' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({ pulls, page, isLoading, error }) => ({
|
||||
pulls,
|
||||
page,
|
||||
isLoading,
|
||||
error
|
||||
const mapStateToProps = ({
|
||||
pulls,
|
||||
builders,
|
||||
page,
|
||||
isLoading,
|
||||
error,
|
||||
build
|
||||
}) => ({
|
||||
pulls,
|
||||
builders,
|
||||
page,
|
||||
isLoading,
|
||||
error,
|
||||
build
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
loadPulls: next => dispatch(loadPulls(next))
|
||||
loadPulls: next => dispatch(loadPulls(next))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Pulls);
|
||||
|
@ -1,50 +1,59 @@
|
||||
import React from 'react';
|
||||
import { UncontrolledCollapse, CardBody, Card, CardHeader } from 'reactstrap';
|
||||
import BuildDetails from './BuildDetails';
|
||||
|
||||
function PullsCard(props) {
|
||||
let tog = 'toggler' + props.id;
|
||||
let createdDate = new Date(props.created_at);
|
||||
let closedDate = new Date(props.closed_at);
|
||||
let mergedDate = new Date(props.merged_at);
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className='new' type='button' id={tog}>
|
||||
<div className='row'>
|
||||
<div className='col-sm'>{props.number}</div>
|
||||
<div className='col-sm'>{props.state}</div>
|
||||
<div className='col-sm'>{props.user.login}</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<UncontrolledCollapse toggler={tog}>
|
||||
<CardBody className='indent'>
|
||||
<p>
|
||||
<strong>Pull number: </strong>{' '}
|
||||
<a target='_blank' rel='noreferrer noopener' href={props.html_url}>
|
||||
{props.number}
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Title:</strong> {props.title}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Body:</strong> {props.body}
|
||||
</p>
|
||||
<div className='row'>
|
||||
<div className='col-sm'>
|
||||
<strong>Created at: </strong>
|
||||
{createdDate.toLocaleString()}
|
||||
</div>
|
||||
<div className='col-sm'>
|
||||
<strong>Closed at: </strong>
|
||||
{props.closed_at !== null ? closedDate.toLocaleString() : null}
|
||||
</div>
|
||||
<div className='col-sm'>
|
||||
<strong>Merged at: </strong>
|
||||
{props.merged_at !== null ? mergedDate.toLocaleString() : null}
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</UncontrolledCollapse>
|
||||
</Card>
|
||||
);
|
||||
let tog = 'toggler' + props.id;
|
||||
let createdDate = new Date(props.created_at);
|
||||
let closedDate = new Date(props.closed_at);
|
||||
let mergedDate = new Date(props.merged_at);
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className='new' type='button' id={tog}>
|
||||
<div className='row'>
|
||||
<div className='col-sm'>{props.number}</div>
|
||||
<div className='col-sm'>{props.state}</div>
|
||||
<div className='col-sm'>{props.user.login}</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<UncontrolledCollapse toggler={tog}>
|
||||
<CardBody className='indent'>
|
||||
<p>
|
||||
<strong>Pull number: </strong>{' '}
|
||||
<a target='_blank' rel='noreferrer noopener' href={props.html_url}>
|
||||
{props.number}
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Title:</strong> {props.title}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Body:</strong> {props.body}
|
||||
</p>
|
||||
<div className='row'>
|
||||
<div className='col-sm'>
|
||||
<strong>Created at: </strong>
|
||||
{createdDate.toLocaleString()}
|
||||
</div>
|
||||
<div className='col-sm'>
|
||||
<strong>Closed at: </strong>
|
||||
{props.closed_at !== null ? closedDate.toLocaleString() : null}
|
||||
</div>
|
||||
<div className='col-sm'>
|
||||
<strong>Merged at: </strong>
|
||||
{props.merged_at !== null ? mergedDate.toLocaleString() : null}
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<h5>Build Details:</h5>
|
||||
{props.builds ? (
|
||||
<BuildDetails builds={props.builds} />
|
||||
) : (
|
||||
<div>Loading Pulls data...</div>
|
||||
)}
|
||||
</CardBody>
|
||||
</UncontrolledCollapse>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
export default PullsCard;
|
||||
|
@ -7,13 +7,15 @@ export const fetchCommits = async (sha, page) => {
|
||||
return data;
|
||||
};
|
||||
|
||||
export const fetchBuildSets = async () => {
|
||||
const response = await fetch('/api/buildsets');
|
||||
const data = await response.json();
|
||||
if (response.status >= 400) {
|
||||
throw new Error(data.errors);
|
||||
export const fetchBuildSets = async str => {
|
||||
if (str) {
|
||||
const response = await fetch(`/api/buildsets?${str}`);
|
||||
const data = await response.json();
|
||||
if (response.status >= 400) {
|
||||
throw new Error(data.errors);
|
||||
}
|
||||
return data.buildsets;
|
||||
}
|
||||
return data.buildsets;
|
||||
};
|
||||
|
||||
export const fetchBuildReq = async str => {
|
||||
|
@ -4,7 +4,7 @@ const builderReducer = (state = {}, action) => {
|
||||
if (action.type === BUILDERS.LOAD_SUCCESS) {
|
||||
const builders = {};
|
||||
for (const builder of action.builders) {
|
||||
builders[action.builders.builderid] = builder;
|
||||
builders[builder.builderid] = builder;
|
||||
}
|
||||
return builders;
|
||||
}
|
||||
|
@ -7,6 +7,21 @@ import { setBuildSetsError, setBuilds } from '../actions';
|
||||
* filtering data on BuildBot side.
|
||||
* see BuildBot API: http://docs.buildbot.net/latest/developer/rest.html#filtering
|
||||
*/
|
||||
|
||||
/* The function convertIsoToUnixTime takes two parameters (committer.data)
|
||||
* first[0] and last commit[9] of the page and converts it to unix time format
|
||||
* and is sent along with fetchBuildSets, and loads only 20-30 datasets into
|
||||
* the memory,which is easy to process.
|
||||
* +5000 is done to normalize the diffrence between committ date and build triggered.
|
||||
* see BuildBot API: http://docs.buildbot.net/latest/developer/rest.html#filtering
|
||||
*/
|
||||
|
||||
function convertIsoToUnixTime(isoF, isoL) {
|
||||
let unixF = Date.parse(isoF.commit.committer.date) / 1000 + 5000;
|
||||
let unixL = Date.parse(isoL.commit.committer.date) / 1000;
|
||||
return '&submitted_at__le=' + unixF + '&submitted_at__ge=' + unixL;
|
||||
}
|
||||
|
||||
function getBuildQString(builds) {
|
||||
return builds
|
||||
.map(build => 'buildrequestid__contains=' + build.buildrequestid)
|
||||
@ -36,7 +51,10 @@ function getBuildReqQString(commits, buildData) {
|
||||
function* handleBuildsLoad() {
|
||||
try {
|
||||
const commits = yield select(state => state.commits);
|
||||
const buildSetsRaw = yield call(fetchBuildSets);
|
||||
const buildSetsRaw = yield call(
|
||||
fetchBuildSets,
|
||||
convertIsoToUnixTime(commits[0], commits[9])
|
||||
);
|
||||
if (buildSetsRaw.length === 0) {
|
||||
yield put(setBuildSetsError('Nothing returned'));
|
||||
return;
|
||||
|
@ -5,13 +5,14 @@ import branchesSaga from './branchesSaga';
|
||||
import pullsSaga from './pullsSaga';
|
||||
import buildersSaga from './buildersSaga';
|
||||
import buildSetSaga from './buildSetSaga';
|
||||
|
||||
import pullsBuildSaga from './pullsBuildSaga';
|
||||
export default function* rootSaga() {
|
||||
yield all([
|
||||
commitsSaga(),
|
||||
branchesSaga(),
|
||||
pullsSaga(),
|
||||
buildersSaga(),
|
||||
buildSetSaga()
|
||||
buildSetSaga(),
|
||||
pullsBuildSaga()
|
||||
]);
|
||||
}
|
||||
|
76
client/src/redux/sagas/pullsBuildSaga.js
Normal file
76
client/src/redux/sagas/pullsBuildSaga.js
Normal file
@ -0,0 +1,76 @@
|
||||
import { takeEvery, call, put, select } from 'redux-saga/effects';
|
||||
import { PULLS } from '../constants';
|
||||
import { fetchBuildSets, fetchBuildReq, fetchBuilds } from '../api';
|
||||
import { setBuildSetsError, setBuilds } from '../actions';
|
||||
|
||||
function convertIsoToUnixTime(isoF, isoL) {
|
||||
let unixF = Date.parse(isoF.created_at) / 1000 + 30000;
|
||||
let unixL = Date.parse(isoL.created_at) / 1000;
|
||||
return '&submitted_at__le=' + unixF + '&submitted_at__ge=' + unixL;
|
||||
}
|
||||
|
||||
function getBuildQString(builds) {
|
||||
return builds
|
||||
.map(build => 'buildrequestid__contains=' + build.buildrequestid)
|
||||
.join('&');
|
||||
}
|
||||
|
||||
function getBuildReqQString(pulls, buildSetsRaw) {
|
||||
var a = pulls
|
||||
.flatMap(pull =>
|
||||
buildSetsRaw.filter(
|
||||
build =>
|
||||
build.sourcestamps[0].branch === `refs/pull/${pull.number}/head` ||
|
||||
build.sourcestamps[0].branch === `refs/pull/${pull.number}/merge`
|
||||
)
|
||||
)
|
||||
.map(bd => 'buildsetid__contains=' + bd.bsid)
|
||||
.join('&');
|
||||
return a;
|
||||
}
|
||||
|
||||
function* handlePullsBuildLoad() {
|
||||
try {
|
||||
const pulls = yield select(state => state.pulls);
|
||||
const buildSetsRaw = yield call(
|
||||
fetchBuildSets,
|
||||
convertIsoToUnixTime(pulls[0], pulls[9])
|
||||
);
|
||||
if (buildSetsRaw.length === 0) {
|
||||
yield put(setBuildSetsError('Nothing returned'));
|
||||
return;
|
||||
}
|
||||
|
||||
const buildReqsRaw = yield call(
|
||||
fetchBuildReq,
|
||||
getBuildReqQString(pulls, buildSetsRaw)
|
||||
);
|
||||
|
||||
const buildsRaw = yield call(fetchBuilds, getBuildQString(buildReqsRaw));
|
||||
|
||||
const buildsByPR = {};
|
||||
for (let { number } of pulls) {
|
||||
const buildSetIds = buildSetsRaw
|
||||
.filter(
|
||||
bs =>
|
||||
bs.sourcestamps[0].branch === `refs/pull/${number}/head` ||
|
||||
bs.sourcestamps[0].branch === `refs/pull/${number}/merge`
|
||||
)
|
||||
.map(bs => bs.bsid);
|
||||
const buildReqIds = buildReqsRaw
|
||||
.filter(br => buildSetIds.includes(br.buildsetid))
|
||||
.map(br => br.buildrequestid);
|
||||
const builds = buildsRaw.filter(b =>
|
||||
buildReqIds.includes(b.buildrequestid)
|
||||
);
|
||||
buildsByPR[number] = builds;
|
||||
}
|
||||
yield put(setBuilds(buildsByPR));
|
||||
} catch (error) {
|
||||
yield put(setBuildSetsError(error.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
export default function* watchPullsBuildLoad() {
|
||||
yield takeEvery(PULLS.LOAD_SUCCESS, handlePullsBuildLoad);
|
||||
}
|
Loading…
Reference in New Issue
Block a user