diff options
author | Tristan Cacqueray <tdecacqu@redhat.com> | 2018-12-06 07:20:51 +0000 |
---|---|---|
committer | Tristan Cacqueray <tdecacqu@redhat.com> | 2018-12-06 07:42:32 +0000 |
commit | 51e525947730262316e21d3f3641e0c818f9d59b (patch) | |
tree | b1cd72cca2f6fab7b98ec37f946e10140aba13e7 | |
parent | 839fe0cceb9f1fb7cb965004bc33b8f2527d0c4e (diff) | |
download | zuul-51e525947730262316e21d3f3641e0c818f9d59b.tar.gz |
web: refactor job page to use a reducer
This change updates the job page component to dispatch reducer
action instead of direct axios call. This enables using the generic
error reducers as well as keeping the tenant jobs in the store to
avoid repeated query.
Change-Id: I142493c3a89379a75d97d247fbe1fcc0858e9723
-rw-r--r-- | web/src/actions/job.js | 67 | ||||
-rw-r--r-- | web/src/pages/Job.jsx | 44 | ||||
-rw-r--r-- | web/src/pages/Jobs.jsx | 2 | ||||
-rw-r--r-- | web/src/reducers/index.js | 2 | ||||
-rw-r--r-- | web/src/reducers/job.js | 55 |
5 files changed, 148 insertions, 22 deletions
diff --git a/web/src/actions/job.js b/web/src/actions/job.js new file mode 100644 index 000000000..06d384c1e --- /dev/null +++ b/web/src/actions/job.js @@ -0,0 +1,67 @@ +/* global Promise */ +// Copyright 2018 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +import * as API from '../api' + +export const JOB_FETCH_REQUEST = 'JOB_FETCH_REQUEST' +export const JOB_FETCH_SUCCESS = 'JOB_FETCH_SUCCESS' +export const JOB_FETCH_FAIL = 'JOB_FETCH_FAIL' + +export const requestJob = () => ({ + type: JOB_FETCH_REQUEST +}) + +export const receiveJob = (tenant, jobname, json) => ({ + type: JOB_FETCH_SUCCESS, + tenant: tenant, + jobname: jobname, + job: json, + receivedAt: Date.now() +}) + +const failedJob = error => ({ + type: JOB_FETCH_FAIL, + error +}) + +const fetchJob = (tenant, jobname) => dispatch => { + dispatch(requestJob()) + return API.fetchJob(tenant.apiPrefix, jobname) + .then(response => dispatch(receiveJob(tenant.name, jobname, response.data))) + .catch(error => dispatch(failedJob(error))) +} + +const shouldFetchJob = (tenant, jobname, state) => { + const tenantJobs = state.job.jobs[tenant.name] + if (tenantJobs) { + const job = tenantJobs[jobname] + if (!job) { + return true + } + if (job.isFetching) { + return false + } + return false + } + return true +} + +export const fetchJobIfNeeded = (tenant, jobname, force) => ( + dispatch, getState) => { + if (force || shouldFetchJob(tenant, jobname, getState())) { + return dispatch(fetchJob(tenant, jobname)) + } + return Promise.resolve() +} diff --git a/web/src/pages/Job.jsx b/web/src/pages/Job.jsx index d4108fe74..8282e2c71 100644 --- a/web/src/pages/Job.jsx +++ b/web/src/pages/Job.jsx @@ -17,31 +17,26 @@ import { connect } from 'react-redux' import PropTypes from 'prop-types' import Job from '../containers/job/Job' -import { fetchJob } from '../api' +import Refreshable from '../containers/Refreshable' +import { fetchJobIfNeeded } from '../actions/job' -class JobPage extends React.Component { +class JobPage extends Refreshable { static propTypes = { match: PropTypes.object.isRequired, - tenant: PropTypes.object + tenant: PropTypes.object, + remoteData: PropTypes.object, + dispatch: PropTypes.func } - state = { - job: null - } - - updateData = () => { - fetchJob(this.props.tenant.apiPrefix, this.props.match.params.jobName) - .then(response => { - this.setState({job: response.data}) - }) + updateData = (force) => { + this.props.dispatch(fetchJobIfNeeded( + this.props.tenant, this.props.match.params.jobName, force)) } componentDidMount () { document.title = 'Zuul Job | ' + this.props.match.params.jobName - if (this.props.tenant.name) { - this.updateData() - } + super.componentDidMount() } componentDidUpdate (prevProps) { @@ -52,14 +47,21 @@ class JobPage extends React.Component { } render () { - const { job } = this.state - if (!job) { - return (<p>Loading...</p>) - } + const { remoteData } = this.props + const tenantJobs = remoteData.jobs[this.props.tenant.name] + const jobName = this.props.match.params.jobName return ( - <Job job={job} /> + <React.Fragment> + <div style={{float: 'right'}}> + {this.renderSpinner()} + </div> + {tenantJobs && tenantJobs[jobName] && <Job job={tenantJobs[jobName]} />} + </React.Fragment> ) } } -export default connect(state => ({tenant: state.tenant}))(JobPage) +export default connect(state => ({ + tenant: state.tenant, + remoteData: state.job, +}))(JobPage) diff --git a/web/src/pages/Jobs.jsx b/web/src/pages/Jobs.jsx index edfd4a3fa..d5a3e2387 100644 --- a/web/src/pages/Jobs.jsx +++ b/web/src/pages/Jobs.jsx @@ -42,7 +42,7 @@ class JobsPage extends Refreshable { const jobs = remoteData.jobs[this.props.tenant.name] return ( <React.Fragment> - <div className="pull-right" style={{display: 'flex'}}> + <div style={{float: 'right'}}> {this.renderSpinner()} </div> {jobs && jobs.length > 0 && diff --git a/web/src/reducers/index.js b/web/src/reducers/index.js index 89822b28e..ccc08a069 100644 --- a/web/src/reducers/index.js +++ b/web/src/reducers/index.js @@ -17,12 +17,14 @@ import { combineReducers } from 'redux' import configErrors from './configErrors' import errors from './errors' import info from './info' +import job from './job' import jobs from './jobs' import status from './status' import tenant from './tenant' const reducers = { info, + job, jobs, configErrors, errors, diff --git a/web/src/reducers/job.js b/web/src/reducers/job.js new file mode 100644 index 000000000..6826b5507 --- /dev/null +++ b/web/src/reducers/job.js @@ -0,0 +1,55 @@ +// Copyright 2018 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +import { + JOB_FETCH_FAIL, + JOB_FETCH_REQUEST, + JOB_FETCH_SUCCESS +} from '../actions/job' + +import update from 'immutability-helper' + +export default (state = { + isFetching: false, + jobs: {}, +}, action) => { + switch (action.type) { + case JOB_FETCH_REQUEST: + return { + isFetching: true, + jobs: state.jobs, + } + case JOB_FETCH_SUCCESS: + if (!state.jobs[action.tenant]) { + state.jobs = update(state.jobs, {$merge: {[action.tenant]: {}}}) + } + return { + isFetching: false, + jobs: update(state.jobs, { + [action.tenant]: { + $merge: { + [action.jobname]: action.job + } + } + }) + } + case JOB_FETCH_FAIL: + return { + isFetching: false, + jobs: state.jobs, + } + default: + return state + } +} |