summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Cacqueray <tdecacqu@redhat.com>2018-12-06 07:20:51 +0000
committerTristan Cacqueray <tdecacqu@redhat.com>2018-12-06 07:42:32 +0000
commit51e525947730262316e21d3f3641e0c818f9d59b (patch)
treeb1cd72cca2f6fab7b98ec37f946e10140aba13e7
parent839fe0cceb9f1fb7cb965004bc33b8f2527d0c4e (diff)
downloadzuul-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.js67
-rw-r--r--web/src/pages/Job.jsx44
-rw-r--r--web/src/pages/Jobs.jsx2
-rw-r--r--web/src/reducers/index.js2
-rw-r--r--web/src/reducers/job.js55
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
+ }
+}