Merge pull request #11792 from marshmalien/8321-job-list-schedule-name

Add schedule detail to job list expanded view
This commit is contained in:
Sarah Akus 2022-03-04 11:46:45 -05:00 committed by GitHub
commit d07c2973e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 193 additions and 35 deletions

View File

@ -1,45 +1,16 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { t } from '@lingui/macro';
import getScheduleUrl from 'util/getScheduleUrl';
import Detail from './Detail';
function getScheduleURL(template, scheduleId, inventoryId = null) {
let scheduleUrl;
switch (template.unified_job_type) {
case 'inventory_update':
scheduleUrl =
inventoryId &&
`/inventories/inventory/${inventoryId}/sources/${template.id}/schedules/${scheduleId}/details`;
break;
case 'job':
scheduleUrl = `/templates/job_template/${template.id}/schedules/${scheduleId}/details`;
break;
case 'project_update':
scheduleUrl = `/projects/${template.id}/schedules/${scheduleId}/details`;
break;
case 'system_job':
scheduleUrl = `/management_jobs/${template.id}/schedules/${scheduleId}/details`;
break;
case 'workflow_job':
scheduleUrl = `/templates/workflow_job_template/${template.id}/schedules/${scheduleId}/details`;
break;
default:
break;
}
return scheduleUrl;
}
const getLaunchedByDetails = ({ summary_fields = {}, launch_type }) => {
const getLaunchedByDetails = (job) => {
const {
created_by: createdBy,
job_template: jobTemplate,
unified_job_template: unifiedJT,
workflow_job_template: workflowJT,
inventory,
schedule,
} = summary_fields;
} = job.summary_fields;
if (!createdBy && !schedule) {
return {};
@ -48,7 +19,7 @@ const getLaunchedByDetails = ({ summary_fields = {}, launch_type }) => {
let link;
let value;
switch (launch_type) {
switch (job.launch_type) {
case 'webhook':
value = t`Webhook`;
link =
@ -58,7 +29,7 @@ const getLaunchedByDetails = ({ summary_fields = {}, launch_type }) => {
break;
case 'scheduled':
value = schedule.name;
link = getScheduleURL(unifiedJT, schedule.id, inventory?.id);
link = getScheduleUrl(job);
break;
case 'manual':
link = `/users/${createdBy.id}/details`;

View File

@ -8,10 +8,16 @@ import { RocketIcon } from '@patternfly/react-icons';
import styled from 'styled-components';
import { formatDateString } from 'util/dates';
import { isJobRunning } from 'util/jobs';
import getScheduleUrl from 'util/getScheduleUrl';
import { ActionsTd, ActionItem, TdBreakWord } from '../PaginatedTable';
import { LaunchButton, ReLaunchDropDown } from '../LaunchButton';
import StatusLabel from '../StatusLabel';
import { DetailList, Detail, LaunchedByDetail } from '../DetailList';
import {
DetailList,
Detail,
DeletedDetail,
LaunchedByDetail,
} from '../DetailList';
import ChipGroup from '../ChipGroup';
import CredentialChip from '../CredentialChip';
import ExecutionEnvironmentDetail from '../ExecutionEnvironmentDetail';
@ -48,6 +54,7 @@ function JobListItem({
job_template,
labels,
project,
schedule,
source_workflow_job,
workflow_job_template,
} = job.summary_fields;
@ -167,6 +174,18 @@ function JobListItem({
/>
)}
<LaunchedByDetail job={job} />
{job.launch_type === 'scheduled' &&
(schedule ? (
<Detail
dataCy="job-schedule"
label={t`Schedule`}
value={
<Link to={getScheduleUrl(job)}>{schedule.name}</Link>
}
/>
) : (
<DeletedDetail label={t`Schedule`} />
))}
{job_template && (
<Detail
label={t`Job Template`}

View File

@ -14,11 +14,20 @@ const mockJob = {
delete: true,
start: true,
},
schedule: {
name: 'mock schedule',
id: 999,
},
unified_job_template: {
unified_job_type: 'job',
id: 1,
},
},
created: '2019-08-08T19:24:05.344276Z',
modified: '2019-08-08T19:24:18.162949Z',
name: 'Demo Job Template',
job_type: 'run',
launch_type: 'scheduled',
started: '2019-08-08T19:24:18.329589Z',
finished: '2019-08-08T19:24:50.119995Z',
status: 'successful',
@ -51,7 +60,11 @@ describe('<JobListItem />', () => {
test('initially renders successfully', () => {
expect(wrapper.find('JobListItem').length).toBe(1);
});
test('should display expected details', () => {
assertDetail('Job Slice', '1/3');
assertDetail('Schedule', 'mock schedule');
});
test('launch button shown to users with launch capabilities', () => {
@ -129,6 +142,25 @@ describe('<JobListItem />', () => {
expect(wrapper.find('Td[dataLabel="Type"]').length).toBe(1);
});
test('should not show schedule detail in expanded view', () => {
wrapper = mountWithContexts(
<table>
<tbody>
<JobListItem
job={{
...mockJob,
summary_fields: {},
}}
showTypeColumn
isSelected
onSelect={() => {}}
/>
</tbody>
</table>
);
expect(wrapper.find('Detail[label="Schedule"] dt').length).toBe(1);
});
test('should not display EE for canceled jobs', () => {
wrapper = mountWithContexts(
<table>

View File

@ -189,6 +189,7 @@ describe('<JobDetail />', () => {
<JobDetail
job={{
...mockJobData,
type: 'workflow_job',
launch_type: 'scheduled',
summary_fields: {
user_capabilities: {},

View File

@ -0,0 +1,32 @@
export default function getScheduleUrl(job) {
const templateId = job.summary_fields.unified_job_template.id;
const scheduleId = job.summary_fields.schedule.id;
const inventoryId = job.summary_fields.inventory
? job.summary_fields.inventory.id
: null;
let scheduleUrl;
switch (job.type) {
case 'inventory_update':
scheduleUrl =
inventoryId &&
`/inventories/inventory/${inventoryId}/sources/${templateId}/schedules/${scheduleId}/details`;
break;
case 'job':
scheduleUrl = `/templates/job_template/${templateId}/schedules/${scheduleId}/details`;
break;
case 'project_update':
scheduleUrl = `/projects/${templateId}/schedules/${scheduleId}/details`;
break;
case 'system_job':
scheduleUrl = `/management_jobs/${templateId}/schedules/${scheduleId}/details`;
break;
case 'workflow_job':
scheduleUrl = `/templates/workflow_job_template/${templateId}/schedules/${scheduleId}/details`;
break;
default:
break;
}
return scheduleUrl;
}

View File

@ -0,0 +1,103 @@
import getScheduleUrl from './getScheduleUrl';
describe('getScheduleUrl', () => {
test('should return expected schedule URL for inventory update job', () => {
const invSrcJob = {
type: 'inventory_update',
summary_fields: {
inventory: {
id: 1,
name: 'mock inv',
},
schedule: {
name: 'mock schedule',
id: 3,
},
unified_job_template: {
unified_job_type: 'inventory_update',
name: 'mock inv src',
id: 2,
},
},
};
expect(getScheduleUrl(invSrcJob)).toEqual(
'/inventories/inventory/1/sources/2/schedules/3/details'
);
});
test('should return expected schedule URL for job', () => {
const templateJob = {
type: 'job',
summary_fields: {
schedule: {
name: 'mock schedule',
id: 5,
},
unified_job_template: {
unified_job_type: 'job',
name: 'mock job',
id: 4,
},
},
};
expect(getScheduleUrl(templateJob)).toEqual(
'/templates/job_template/4/schedules/5/details'
);
});
test('should return expected schedule URL for project update job', () => {
const projectUpdateJob = {
type: 'project_update',
summary_fields: {
schedule: {
name: 'mock schedule',
id: 7,
},
unified_job_template: {
unified_job_type: 'project_update',
name: 'mock job',
id: 6,
},
},
};
expect(getScheduleUrl(projectUpdateJob)).toEqual(
'/projects/6/schedules/7/details'
);
});
test('should return expected schedule URL for system job', () => {
const systemJob = {
type: 'system_job',
summary_fields: {
schedule: {
name: 'mock schedule',
id: 10,
},
unified_job_template: {
unified_job_type: 'system_job',
name: 'mock job',
id: 9,
},
},
};
expect(getScheduleUrl(systemJob)).toEqual(
'/management_jobs/9/schedules/10/details'
);
});
test('should return expected schedule URL for workflow job', () => {
const workflowJob = {
type: 'workflow_job',
summary_fields: {
schedule: {
name: 'mock schedule',
id: 12,
},
unified_job_template: {
unified_job_type: 'job',
name: 'mock job',
id: 11,
},
},
};
expect(getScheduleUrl(workflowJob)).toEqual(
'/templates/workflow_job_template/11/schedules/12/details'
);
});
});