Add system prompt and config

This commit is contained in:
Jake McDermott 2021-02-03 14:59:32 -05:00
parent a95e554a16
commit a07b1a19f3
No known key found for this signature in database
GPG Key ID: 0E56ED990CDFCB4F
6 changed files with 267 additions and 65 deletions

View File

@ -3359,6 +3359,13 @@ class SystemJobTemplateSerializer(UnifiedJobTemplateSerializer):
del result['default_days']
return result
def __init__(self, *args, **kwargs):
super(SystemJobTemplateSerializer, self).__init__(*args, **kwargs)
for field_name, field_instance in self.fields.items():
if field_name != 'default_days':
field_instance.read_only = True
class SystemJobSerializer(UnifiedJobSerializer):

View File

@ -3423,7 +3423,7 @@ class SystemJobTemplateList(ListAPIView):
return super(SystemJobTemplateList, self).get(request, *args, **kwargs)
class SystemJobTemplateDetail(RetrieveAPIView):
class SystemJobTemplateDetail(RetrieveUpdateAPIView):
model = models.SystemJobTemplate
serializer_class = serializers.SystemJobTemplateSerializer

View File

@ -1,9 +1,71 @@
import React from 'react';
import React, { useState } from 'react';
import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { Formik } from 'formik';
import { Form } from '@patternfly/react-core';
import { useHistory } from 'react-router-dom';
import { SystemJobTemplatesAPI } from '../../../api';
import FormField, { FormSubmitError } from '../../../components/FormField';
import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
import { FormColumnLayout } from '../../../components/FormLayout';
import { minMaxValue } from '../../../util/validators';
import { CardBody } from '../../../components/Card';
function ManagementJobEdit() {
return <CardBody>Management Job Edit</CardBody>;
function ManagementJobEdit({ i18n, managementJob }) {
const history = useHistory();
const [formError, setFormError] = useState(null);
const handleCancel = () => {
history.push(`/management_jobs/${managementJob?.id}/details`);
};
const handleSubmit = async values => {
try {
await SystemJobTemplatesAPI.update(managementJob?.id, {
default_days: values.dataRetention,
});
history.push(`/management_jobs/${managementJob?.id}/details`);
} catch (error) {
setFormError(error);
}
};
return (
<CardBody>
{managementJob?.default_days ? (
<Formik
initialValues={{
dataRetention: managementJob?.default_days || null,
description: i18n._(t`Delete data older than this number of days.`),
}}
onSubmit={handleSubmit}
>
{formik => (
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
<FormColumnLayout>
<FormField
id="data-retention"
name="dataRetention"
type="number"
label={i18n._(t`Data Retention (Days)`)}
tooltip={i18n._(
t`Delete data older than this number of days.`
)}
validate={minMaxValue(0, Number.MAX_SAFE_INTEGER, i18n)}
/>
<FormSubmitError error={formError} />
<FormActionGroup
onCancel={handleCancel}
onSubmit={formik.handleSubmit}
/>
</FormColumnLayout>
</Form>
)}
</Formik>
) : null}
</CardBody>
);
}
export default ManagementJobEdit;
export default withI18n()(ManagementJobEdit);

View File

@ -0,0 +1,83 @@
import React, { useState } from 'react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Button, TextInput, Tooltip } from '@patternfly/react-core';
import { RocketIcon } from '@patternfly/react-icons';
import AlertModal from '../../../components/AlertModal';
const clamp = (val, min, max) => {
if (val < min) {
return min;
}
if (val > max) {
return max;
}
return val;
};
function LaunchManagementPrompt({
i18n,
isOpen,
isLoading,
onClick,
onClose,
onConfirm,
defaultDays,
}) {
const [dataRetention, setDataRetention] = useState(defaultDays);
return (
<>
<Tooltip content={i18n._(t`Launch management job`)} position="top">
<Button
aria-label={i18n._(t`Launch management job`)}
variant="plain"
onClick={onClick}
isDisabled={isLoading}
>
<RocketIcon />
</Button>
</Tooltip>
<AlertModal
isOpen={isOpen}
variant="info"
onClose={onClose}
title={i18n._(t`Launch management job`)}
label={i18n._(t`Launch management job`)}
actions={[
<Button
id="launch-job-confirm-button"
key="delete"
variant="primary"
isDisabled={isLoading}
aria-label={i18n._(t`Launch`)}
onClick={() => onConfirm(dataRetention)}
>
{i18n._(t`Launch`)}
</Button>,
<Button
id="launch-job-cancel-button"
key="cancel"
variant="secondary"
aria-label={i18n._(t`Cancel`)}
onClick={onClose}
>
{i18n._(t`Cancel`)}
</Button>,
]}
>
{i18n._(t`Set how many days of data should be retained.`)}
<TextInput
value={dataRetention}
type="number"
onChange={value =>
setDataRetention(clamp(value, 0, Number.MAX_SAFE_INTEGER))
}
aria-label={i18n._(t`Launch`)}
/>
</AlertModal>
</>
);
}
export default withI18n()(LaunchManagementPrompt);

View File

@ -91,6 +91,7 @@ function ManagementJobList({ i18n }) {
name,
description,
has_configurable_retention,
default_days,
}) => (
<ManagementJobListItem
key={id}
@ -99,6 +100,7 @@ function ManagementJobList({ i18n }) {
description={description}
isSuperUser={me?.is_superuser}
isConfigurable={has_configurable_retention}
defaultDays={default_days}
onLaunchError={setLaunchError}
/>
)}

View File

@ -11,10 +11,13 @@ import {
DataListItemCells,
Tooltip,
} from '@patternfly/react-core';
import { PencilAltIcon, RocketIcon } from '@patternfly/react-icons';
import { RocketIcon, PencilAltIcon } from '@patternfly/react-icons';
import styled from 'styled-components';
import { SystemJobTemplatesAPI } from '../../../api';
import AlertModal from '../../../components/AlertModal';
import ErrorDetail from '../../../components/ErrorDetail';
import LaunchManagementPrompt from './LaunchManagementPrompt';
const DataListAction = styled(_DataListAction)`
align-items: center;
@ -31,6 +34,7 @@ function ManagementJobListItem({
id,
name,
description,
defaultDays,
}) {
const detailsUrl = `/management_jobs/${id}/details`;
const editUrl = `/management_jobs/${id}/edit`;
@ -39,6 +43,28 @@ function ManagementJobListItem({
const history = useHistory();
const [isLaunchLoading, setIsLaunchLoading] = useState(false);
const [isManagementPromptOpen, setIsManagementPromptOpen] = useState(false);
const [isManagementPromptLoading, setIsManagementPromptLoading] = useState(
false
);
const [managementPromptError, setManagementPromptError] = useState(null);
const handleManagementPromptClick = () => setIsManagementPromptOpen(true);
const handleManagementPromptClose = () => setIsManagementPromptOpen(false);
const handleManagementPromptConfirm = async days => {
setIsManagementPromptLoading(true);
try {
const { data } = await SystemJobTemplatesAPI.launch(id, {
extra_vars: { days },
});
history.push(`/jobs/management/${data.id}/output`);
} catch (error) {
setManagementPromptError(error);
} finally {
setIsManagementPromptLoading(false);
}
};
const handleLaunch = async () => {
setIsLaunchLoading(true);
try {
@ -52,67 +78,89 @@ function ManagementJobListItem({
};
return (
<DataListItem key={id} id={id} aria-labelledby={labelId}>
<DataListItemRow>
<DataListItemCells
dataListCells={[
<DataListCell
key="name"
aria-label={i18n._(t`management job name`)}
>
<Link to={detailsUrl}>
<b>{name}</b>
</Link>
</DataListCell>,
<DataListCell
key="description"
aria-label={i18n._(t`management job description`)}
>
<strong>{i18n._(t`Description:`)}</strong> {description}
</DataListCell>,
]}
/>
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{isSuperUser ? (
<>
<Tooltip
content={i18n._(t`Launch management job`)}
position="top"
<>
<DataListItem key={id} id={id} aria-labelledby={labelId}>
<DataListItemRow>
<DataListItemCells
dataListCells={[
<DataListCell
key="name"
aria-label={i18n._(t`management job name`)}
>
<Button
aria-label={i18n._(t`Launch management job`)}
variant="plain"
onClick={handleLaunch}
isDisabled={isLaunchLoading}
>
<RocketIcon />
</Button>
</Tooltip>
{isConfigurable ? (
<Tooltip
content={i18n._(t`Edit management job`)}
position="top"
>
<Button
aria-label={i18n._(t`Edit management job`)}
variant="plain"
component={Link}
to={editUrl}
isDisabled={isLaunchLoading}
<Link to={detailsUrl}>
<b>{name}</b>
</Link>
</DataListCell>,
<DataListCell
key="description"
aria-label={i18n._(t`management job description`)}
>
<strong>{i18n._(t`Description:`)}</strong> {description}
</DataListCell>,
]}
/>
<DataListAction aria-labelledby={labelId} id={labelId}>
{isSuperUser ? (
<>
{isConfigurable ? (
<>
<LaunchManagementPrompt
isOpen={isManagementPromptOpen}
isLoading={isManagementPromptLoading}
onClick={handleManagementPromptClick}
onClose={handleManagementPromptClose}
onConfirm={handleManagementPromptConfirm}
defaultDays={defaultDays}
/>
<Tooltip
content={i18n._(t`Edit management job`)}
position="top"
>
<Button
aria-label={i18n._(t`Edit management job`)}
variant="plain"
component={Link}
to={editUrl}
isDisabled={isLaunchLoading}
>
<PencilAltIcon />
</Button>
</Tooltip>
</>
) : (
<Tooltip
content={i18n._(t`Launch management job`)}
position="top"
>
<PencilAltIcon />
</Button>
</Tooltip>
) : null}
</>
) : null}
</DataListAction>
</DataListItemRow>
</DataListItem>
<Button
aria-label={i18n._(t`Launch management job`)}
variant="plain"
onClick={handleLaunch}
isDisabled={isLaunchLoading}
>
<RocketIcon />
</Button>
</Tooltip>
)}{' '}
</>
) : null}
</DataListAction>
</DataListItemRow>
</DataListItem>
{managementPromptError && (
<>
<AlertModal
isOpen={managementPromptError}
variant="danger"
onClose={() => setManagementPromptError(null)}
title={i18n._(t`Management job launch error`)}
label={i18n._(t`Management job launch error`)}
>
<ErrorDetail error={managementPromptError} />
</AlertModal>
</>
)}
</>
);
}