Skip to content

Commit f95fd57

Browse files
committed
fix(manager): update validation errors to use graphql
1 parent e7a7aa6 commit f95fd57

File tree

6 files changed

+164
-31
lines changed

6 files changed

+164
-31
lines changed

lib/manager/actions/versions.js

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,84 @@ export function receiveValidationResult (feedVersion, validationResult) {
188188
validationResult
189189
}
190190
}
191+
192+
const fetchingValidationErrors = createAction('FETCHING_VALIDATION_ERRORS')
193+
194+
const receiveValidationErrors = createAction('RECEIVE_VALIDATION_ERRORS')
195+
196+
export function fetchValidationErrors ({feedVersion, errorType, limit, offset}) {
197+
return function (dispatch, getState) {
198+
dispatch(fetchingValidationErrors)
199+
// FIXME: why does namespace need to appear twice?
200+
const query = `
201+
query errorsQuery($namespace: String, $errorType: [String], $limit: Int, $offset: Int) {
202+
feed(namespace: $namespace) {
203+
feed_id
204+
feed_version
205+
filename
206+
errors (error_type: $errorType, limit: $limit, offset: $offset) {
207+
error_type
208+
entity_type
209+
entity_id
210+
line_number
211+
bad_value
212+
}
213+
}
214+
}
215+
`
216+
const {namespace} = feedVersion
217+
const method = 'post'
218+
const body = JSON.stringify({
219+
query,
220+
variables: JSON.stringify({namespace, errorType: [errorType], limit, offset})
221+
})
222+
return fetch(GTFS_GRAPHQL_PREFIX, {method, body})
223+
.then(response => response.json())
224+
.then(result => {
225+
if (result.feed) {
226+
const {errors} = result.feed
227+
dispatch(receiveValidationErrors({feedVersion, errorType, limit, offset, errors}))
228+
}
229+
})
230+
.catch(err => console.log(err))
231+
}
232+
}
233+
191234
export function fetchValidationResult (feedVersion, isPublic) {
192235
return function (dispatch, getState) {
193236
dispatch(requestingValidationResult(feedVersion))
194-
const route = isPublic ? 'public' : 'secure'
195-
const url = `/api/manager/${route}/feedversion/${feedVersion.id}/validation`
196-
return dispatch(secureFetch(url))
237+
const {namespace} = feedVersion
238+
const query = `
239+
query countsQuery($namespace: String) {
240+
feed(namespace: $namespace) {
241+
feed_id
242+
feed_version
243+
filename
244+
row_counts {
245+
stops
246+
trips
247+
calendar_dates
248+
errors
249+
}
250+
error_counts {
251+
type, count
252+
}
253+
}
254+
}
255+
`
256+
const method = 'post'
257+
const body = JSON.stringify({
258+
query,
259+
variables: JSON.stringify({namespace})
260+
})
261+
return fetch(GTFS_GRAPHQL_PREFIX, {method, body})
197262
.then(response => response.json())
198263
.then(result => {
199-
dispatch(receiveValidationResult(feedVersion, result))
264+
if (result.feed) {
265+
dispatch(receiveValidationResult(feedVersion, result.feed))
266+
}
200267
})
268+
.catch(err => console.log(err))
201269
}
202270
}
203271

lib/manager/components/validation/GtfsValidationSummary.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class ValidationSummaryTable extends Component {
5555
const {
5656
version
5757
} = this.props
58-
if (version && version.validationResult && version.validationResult.errors.length === 0) {
58+
if (version && version.validationResult && version.validationResult.error_counts.length === 0) {
5959
return <div className='lead text-center '>No validation issues found.</div>
6060
} else if (!version || !version.validationResult) {
6161
return <div className='lead text-center '>Feed has not yet been validated.</div>
@@ -65,7 +65,7 @@ export class ValidationSummaryTable extends Component {
6565
overflowWrap: 'break-word'
6666
}
6767
const problemMap = {}
68-
version.validationResult.errors && version.validationResult.errors.map(val => {
68+
version.validationResult.error_counts && version.validationResult.error_counts.map(val => {
6969
if (!problemMap[val.errorType]) {
7070
problemMap[val.errorType] = {
7171
count: 0,

lib/manager/components/validation/GtfsValidationViewer.js

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
1+
import Icon from '@conveyal/woonerf/components/icon'
12
import moment from 'moment'
23
import React, {Component, PropTypes} from 'react'
3-
import {
4-
// Badge,
5-
// Button
6-
} from 'react-bootstrap'
4+
import {ListGroup, ListGroupItem, Panel} from 'react-bootstrap'
75
import BootstrapTable from 'react-bootstrap-table/lib/BootstrapTable'
86
import TableHeaderColumn from 'react-bootstrap-table/lib/TableHeaderColumn'
97

10-
import {ValidationSummaryTable} from './GtfsValidationSummary'
8+
import OptionButton from '../../../common/components/OptionButton'
9+
10+
const DEFAULT_LIMIT = 10
1111

1212
export default class GtfsValidationViewer extends Component {
1313
static propTypes = {
1414
fetchValidationResult: PropTypes.func,
1515
validationResult: PropTypes.object,
1616
version: PropTypes.object
1717
}
18+
state = {
19+
offset: 0,
20+
limit: DEFAULT_LIMIT
21+
}
1822
componentWillMount () {
1923
this.props.fetchValidationResult()
2024
}
@@ -40,11 +44,35 @@ export default class GtfsValidationViewer extends Component {
4044
formatter (cell, row) {
4145
return <span title={cell}>{cell}</span>
4246
}
47+
48+
_onClickErrorType = errorType => {
49+
const {version: feedVersion, fetchValidationErrors} = this.props
50+
const {active} = this.state
51+
if (active === errorType) {
52+
this.setState({active: null})
53+
} else {
54+
const offset = 0
55+
const limit = DEFAULT_LIMIT
56+
// reset active error type, limit, and offset
57+
this.setState({active: errorType, limit, offset})
58+
fetchValidationErrors({feedVersion, errorType, offset, limit})
59+
}
60+
}
61+
62+
_onClickLoadMoreErrors = errorType => {
63+
const {version: feedVersion, fetchValidationErrors} = this.props
64+
const {limit} = this.state
65+
const offset = this.state.offset * limit + 1
66+
this.setState({offset})
67+
fetchValidationErrors({feedVersion, errorType, offset, limit})
68+
}
69+
4370
render () {
4471
const {
4572
validationResult: result,
4673
version
4774
} = this.props
75+
const {active} = this.state
4876
const dateFormat = 'MMM. DD, YYYY'
4977
const timeFormat = 'h:MMa'
5078
// const messages = getComponentMessages('GtfsValidationViewer')
@@ -61,21 +89,58 @@ export default class GtfsValidationViewer extends Component {
6189
sizePerPageList: [10, 20, 50, 100]
6290
}
6391
}
64-
// let report = null
65-
const files = ['routes', 'stops', 'trips', 'shapes', 'stop_times']
66-
const errors = {}
67-
result && result.errors.map((error, i) => {
68-
error.index = i
69-
const key = files.indexOf(error.file) !== -1 ? error.file : 'other'
70-
if (!errors[error.file]) {
71-
errors[key] = []
72-
}
73-
errors[key].push(error)
74-
})
92+
const hasValidation = result && result.error_counts
93+
const hasErrors = hasValidation && result.error_counts.length
94+
const listGroupItemStyle = { fontSize: '18px', textAlign: 'center' }
7595
return (
7696
<div>
7797
<h2 style={{marginTop: '0px'}}>{version.name} <small>{moment(version.updated).format(dateFormat + ', ' + timeFormat)}</small></h2>
78-
<ValidationSummaryTable version={version} />
98+
<Panel header={<h2>Validation errors</h2>}>
99+
{hasErrors
100+
? result.error_counts.map((category, index) => {
101+
const activeWithErrors = category.errors && active === category.type
102+
return (
103+
<ListGroup key={index} fill>
104+
<ListGroupItem style={listGroupItemStyle}>
105+
{category.type}: {category.count}
106+
{' '}
107+
<OptionButton
108+
value={category.type}
109+
onClick={this._onClickErrorType}>
110+
<Icon type={`caret-${active === category.type ? 'up' : 'down'}`} />
111+
</OptionButton>
112+
</ListGroupItem>
113+
{activeWithErrors
114+
? category.errors.map((error, index) => (
115+
<ListGroupItem key={index} style={listGroupItemStyle}>
116+
line: {error.line_number}{' '}
117+
entity_type: {error.entity_type}{' '}
118+
entity_id: {error.entity_id}{' '}
119+
bad_value: {error.bad_value}
120+
</ListGroupItem>
121+
))
122+
: null
123+
}
124+
{activeWithErrors && category.errors.length < category.count
125+
? <ListGroupItem style={listGroupItemStyle}>
126+
<OptionButton
127+
value={category.type}
128+
onClick={this._onClickLoadMoreErrors}>
129+
Load more
130+
</OptionButton>
131+
</ListGroupItem>
132+
: activeWithErrors
133+
? <ListGroupItem style={listGroupItemStyle}>
134+
No more errors of this type
135+
</ListGroupItem>
136+
: null
137+
}
138+
</ListGroup>
139+
)
140+
})
141+
: null
142+
}
143+
</Panel>
79144
<BootstrapTable
80145
data={result && result.errors ? result.errors : []}
81146
{...tableOptions}

lib/manager/components/version/FeedVersionNavigator.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export default class FeedVersionNavigator extends Component {
2929
fetchValidationResult: PropTypes.func,
3030
setVersionIndex: PropTypes.func,
3131
sortedVersions: PropTypes.array,
32+
user: PropTypes.object,
3233
validationJob: PropTypes.object,
3334
version: PropTypes.object
3435
}
@@ -105,10 +106,9 @@ export default class FeedVersionNavigator extends Component {
105106
isPublic,
106107
sortedVersions,
107108
user,
108-
version,
109109
versionSection
110110
} = this.props
111-
const versions = feedSource.feedVersions
111+
const {feedVersions: versions} = feedSource
112112
const messages = getComponentMessages('FeedVersionNavigator')
113113

114114
if (typeof feedVersionIndex === 'undefined') return null
@@ -216,9 +216,8 @@ export default class FeedVersionNavigator extends Component {
216216
<Row>
217217
<Col xs={12}>
218218
<FeedVersionViewer
219+
{...this.props}
219220
isPublic={isPublic}
220-
feedSource={feedSource}
221-
version={version}
222221
feedVersionIndex={feedVersionIndex}
223222
versionSection={versionSection || null}
224223
versions={versions}
@@ -228,8 +227,7 @@ export default class FeedVersionNavigator extends Component {
228227
gtfsPlusDataRequested={this._onRequestGtfsPlusData}
229228
notesRequested={this._onVersionNotesRequested}
230229
newNotePosted={this._onVersionNotePosted}
231-
user={user}
232-
{...this.props} />
230+
user={user} />
233231
</Col>
234232
</Row>
235233
</div>

lib/manager/components/version/FeedVersionViewer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ export default class FeedVersionViewer extends Component {
7171
? <FeedVersionReport {...this.props} />
7272
: versionSection === 'issues'
7373
? <GtfsValidationViewer
74+
{...this.props}
7475
validationResult={version.validationResult}
75-
version={version}
7676
fetchValidationResult={this._onFetchValidation} />
7777
: versionSection === 'gtfsplus' && isModuleEnabled('gtfsplus')
7878
? <ActiveGtfsPlusVersionSummary

lib/manager/containers/ActiveFeedVersionNavigator.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import FeedVersionNavigator from '../components/version/FeedVersionNavigator'
55
import {
66
deleteFeedVersion,
77
downloadFeedViaToken,
8+
fetchValidationErrors,
89
fetchValidationResult,
910
renameFeedVersion,
1011
setActiveVersion,
@@ -40,7 +41,7 @@ const mapStateToProps = (state, ownProps) => {
4041
}
4142
const { jobs } = state.status.jobMonitor
4243
const validationJob = feedVersionIndex >= 1
43-
? jobs.find(j => j.type === 'VALIDATE_FEED' && j.feedVersion.id === feedVersions[feedVersionIndex - 1].id)
44+
? jobs.find(j => j.type === 'VALIDATE_FEED' && j.feedVersionId === feedVersions[feedVersionIndex - 1].id)
4445
: null
4546

4647
const hasVersions = feedVersions && feedVersions.length > 0
@@ -89,6 +90,7 @@ const mapDispatchToProps = (dispatch, ownProps) => {
8990
newNotePostedForVersion: (feedVersion, note) => dispatch(postNoteForFeedVersion(feedVersion, note)),
9091
notesRequestedForVersion: (feedVersion) => dispatch(fetchNotesForFeedVersion(feedVersion)),
9192
fetchValidationResult: (feedVersion, isPublic) => dispatch(fetchValidationResult(feedVersion, isPublic)),
93+
fetchValidationErrors: payload => dispatch(fetchValidationErrors(payload)),
9294
publishFeedVersion: (feedVersion) => dispatch(publishFeedVersion(feedVersion))
9395
}
9496
}

0 commit comments

Comments
 (0)