Merge "web UI: allow a privileged user to promote a change"
This commit is contained in:
commit
dd2ba8fd6e
|
@ -15,6 +15,7 @@
|
|||
export const ADMIN_DEQUEUE_FAIL = 'ADMIN_DEQUEUE_FAIL'
|
||||
export const ADMIN_ENQUEUE_FAIL = 'ADMIN_ENQUEUE_FAIL'
|
||||
export const ADMIN_AUTOHOLD_FAIL = 'ADMIN_AUTOHOLD_FAIL'
|
||||
export const ADMIN_PROMOTE_FAIL = 'ADMIN_PROMOTE_FAIL'
|
||||
|
||||
export const addDequeueError = error => ({
|
||||
type: ADMIN_DEQUEUE_FAIL,
|
||||
|
@ -30,3 +31,8 @@ export const addAutoholdError = error => ({
|
|||
type: ADMIN_AUTOHOLD_FAIL,
|
||||
notification: error
|
||||
})
|
||||
|
||||
export const addPromoteError = error => ({
|
||||
type: ADMIN_PROMOTE_FAIL,
|
||||
notification: error
|
||||
})
|
|
@ -278,6 +278,21 @@ function autohold_delete(apiPrefix, requestId, token) {
|
|||
return res
|
||||
}
|
||||
|
||||
function promote(apiPrefix, pipeline, changes, token) {
|
||||
const instance = Axios.create({
|
||||
baseURL: apiUrl
|
||||
})
|
||||
instance.defaults.headers.common['Authorization'] = 'Bearer ' + token
|
||||
let res = instance.post(
|
||||
apiPrefix + '/promote',
|
||||
{
|
||||
pipeline: pipeline,
|
||||
changes: changes,
|
||||
}
|
||||
)
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
apiUrl,
|
||||
|
@ -310,4 +325,5 @@ export {
|
|||
dequeue_ref,
|
||||
enqueue,
|
||||
enqueue_ref,
|
||||
promote,
|
||||
}
|
||||
|
|
|
@ -26,12 +26,14 @@ import {
|
|||
ModalVariant
|
||||
} from '@patternfly/react-core'
|
||||
import {
|
||||
AngleDoubleUpIcon,
|
||||
BanIcon,
|
||||
} from '@patternfly/react-icons'
|
||||
import { dequeue, dequeue_ref } from '../../api'
|
||||
import { addDequeueError } from '../../actions/adminActions'
|
||||
import { dequeue, dequeue_ref, promote } from '../../api'
|
||||
import { addDequeueError, addPromoteError } from '../../actions/adminActions'
|
||||
|
||||
import { addNotification } from '../../actions/notifications'
|
||||
import { fetchStatusIfNeeded } from '../../actions/status'
|
||||
|
||||
import LineAngleImage from '../../images/line-angle.png'
|
||||
import LineTImage from '../../images/line-t.png'
|
||||
|
@ -43,7 +45,7 @@ class Change extends React.Component {
|
|||
change: PropTypes.object.isRequired,
|
||||
queue: PropTypes.object.isRequired,
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
pipeline: PropTypes.string,
|
||||
pipeline: PropTypes.object,
|
||||
tenant: PropTypes.object,
|
||||
user: PropTypes.object,
|
||||
dispatch: PropTypes.func
|
||||
|
@ -51,6 +53,7 @@ class Change extends React.Component {
|
|||
|
||||
state = {
|
||||
showDequeueModal: false,
|
||||
showPromoteModal: false,
|
||||
showAdminActions: false,
|
||||
}
|
||||
|
||||
|
@ -63,12 +66,18 @@ class Change extends React.Component {
|
|||
// post-merge
|
||||
if (/^[0-9a-f]{40}$/.test(changeId)) {
|
||||
dequeue_ref(tenant.apiPrefix, projectName, pipeline.name, changeRef, user.token)
|
||||
.then(() => {
|
||||
this.props.dispatch(fetchStatusIfNeeded(tenant))
|
||||
})
|
||||
.catch(error => {
|
||||
this.props.dispatch(addDequeueError(error))
|
||||
})
|
||||
// pre-merge, ie we have a change id
|
||||
} else if (changeId !== 'N/A') {
|
||||
dequeue(tenant.apiPrefix, projectName, pipeline.name, changeId, user.token)
|
||||
.then(() => {
|
||||
this.props.dispatch(fetchStatusIfNeeded(tenant))
|
||||
})
|
||||
.catch(error => {
|
||||
this.props.dispatch(addDequeueError(error))
|
||||
})
|
||||
|
@ -108,13 +117,59 @@ class Change extends React.Component {
|
|||
)
|
||||
}
|
||||
|
||||
promoteConfirm = () => {
|
||||
const { tenant, user, change, pipeline } = this.props
|
||||
let changeId = change.id || 'NA'
|
||||
this.setState(() => ({ showPromoteModal: false }))
|
||||
if (changeId !== 'N/A') {
|
||||
promote(tenant.apiPrefix, pipeline.name, [changeId,], user.token)
|
||||
.then(() => {
|
||||
this.props.dispatch(fetchStatusIfNeeded(tenant))
|
||||
})
|
||||
.catch(error => {
|
||||
this.props.dispatch(addPromoteError(error))
|
||||
})
|
||||
} else {
|
||||
this.props.dispatch(addNotification({
|
||||
url: null,
|
||||
status: 'Invalid change ' + changeId + ' for promotion',
|
||||
text: '',
|
||||
type: 'error'
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
promoteCancel = () => {
|
||||
this.setState(() => ({ showPromoteModal: false }))
|
||||
}
|
||||
|
||||
renderPromoteModal() {
|
||||
const { showPromoteModal } = this.state
|
||||
const { change } = this.props
|
||||
let changeId = change.id || 'N/A'
|
||||
const title = 'You are about to promote a change'
|
||||
return (
|
||||
<Modal
|
||||
variant={ModalVariant.small}
|
||||
// titleIconVariant={BullhornIcon}
|
||||
isOpen={showPromoteModal}
|
||||
title={title}
|
||||
onClose={this.promoteCancel}
|
||||
actions={[
|
||||
<Button key="prom_confirm" variant="primary" onClick={this.promoteConfirm}>Confirm</Button>,
|
||||
<Button key="prom_cancel" variant="link" onClick={this.promoteCancel}>Cancel</Button>,
|
||||
]}>
|
||||
<p>Please confirm that you want to promote change <strong>{changeId}</strong>.</p>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
renderAdminCommands(idx) {
|
||||
const { showAdminActions } = this.state
|
||||
const { queue } = this.props
|
||||
const { queue, pipeline } = this.props
|
||||
const dropdownCommands = [
|
||||
<DropdownItem
|
||||
key="dequeue"
|
||||
|
||||
icon={<BanIcon style={{
|
||||
color: 'var(--pf-global--danger-color--100)',
|
||||
}} />}
|
||||
|
@ -125,6 +180,21 @@ class Change extends React.Component {
|
|||
}}
|
||||
>Dequeue</DropdownItem>,
|
||||
]
|
||||
if (pipeline.manager === 'dependent') {
|
||||
dropdownCommands.push(
|
||||
<DropdownItem
|
||||
key="promote"
|
||||
icon={<AngleDoubleUpIcon style={{
|
||||
color: 'var(--pf-global--default-color--200)',
|
||||
}} />}
|
||||
description="Promote this change to the top of the queue"
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
this.setState(() => ({ showPromoteModal: true }))
|
||||
}}
|
||||
>Promote</DropdownItem>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Dropdown
|
||||
title='Actions'
|
||||
|
@ -238,6 +308,7 @@ class Change extends React.Component {
|
|||
</tbody>
|
||||
</table>
|
||||
{this.renderDequeueModal()}
|
||||
{this.renderPromoteModal()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -339,7 +339,7 @@ class ChangePanel extends React.Component {
|
|||
</div>
|
||||
</div>
|
||||
{expand ? this.renderJobList(change.jobs) : ''}
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
|
|
@ -20,15 +20,15 @@ import Change from './Change'
|
|||
|
||||
class ChangeQueue extends React.Component {
|
||||
static propTypes = {
|
||||
pipeline: PropTypes.string.isRequired,
|
||||
pipeline: PropTypes.object.isRequired,
|
||||
queue: PropTypes.object.isRequired,
|
||||
expanded: PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { queue, pipeline, expanded } = this.props
|
||||
let fullName = queue.name
|
||||
if(queue.branch) {
|
||||
if (queue.branch) {
|
||||
fullName = `${fullName} (${queue.branch})`
|
||||
}
|
||||
let shortName = fullName
|
||||
|
@ -49,7 +49,7 @@ class ChangeQueue extends React.Component {
|
|||
})
|
||||
})
|
||||
return (
|
||||
<div className="change-queue" data-zuul-pipeline={pipeline}>
|
||||
<div className="change-queue" data-zuul-pipeline={pipeline.name}>
|
||||
<p>Queue: <abbr title={fullName}>{shortName}</abbr></p>
|
||||
{changesList}
|
||||
</div>)
|
||||
|
|
|
@ -214,7 +214,7 @@ class Pipeline extends React.Component {
|
|||
<ChangeQueue
|
||||
queue={changeQueue}
|
||||
expanded={expanded}
|
||||
pipeline={pipeline.name}
|
||||
pipeline={pipeline}
|
||||
key={changeQueue.uuid}
|
||||
/>
|
||||
))}
|
||||
|
|
Loading…
Reference in New Issue