Merge "web: refactor job page to use a reducer"

This commit is contained in:
Zuul 2018-12-07 16:32:41 +00:00 committed by Gerrit Code Review
commit 2645360b65
5 changed files with 148 additions and 22 deletions

67
web/src/actions/job.js Normal file
View File

@ -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()
}

View File

@ -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)

View File

@ -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 &&

View File

@ -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,

55
web/src/reducers/job.js Normal file
View File

@ -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
}
}