mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 18:09:57 -03:30
Adds source data to job list and job details view
This commit is contained in:
parent
86390152bc
commit
48a044cc68
@ -12,7 +12,7 @@ import useSelected from 'hooks/useSelected';
|
||||
import useExpanded from 'hooks/useExpanded';
|
||||
import { isJobRunning, getJobModel } from 'util/jobs';
|
||||
import { getQSConfig, parseQueryString } from 'util/qs';
|
||||
import { UnifiedJobsAPI } from 'api';
|
||||
import { UnifiedJobsAPI, InventorySourcesAPI } from 'api';
|
||||
import AlertModal from '../AlertModal';
|
||||
import DatalistToolbar from '../DataListToolbar';
|
||||
import ErrorDetail from '../ErrorDetail';
|
||||
@ -41,7 +41,13 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
const { me } = useConfig();
|
||||
const location = useLocation();
|
||||
const {
|
||||
result: { results, count, relatedSearchableKeys, searchableKeys },
|
||||
result: {
|
||||
results,
|
||||
count,
|
||||
relatedSearchableKeys,
|
||||
searchableKeys,
|
||||
inventorySourceChoices,
|
||||
},
|
||||
error: contentError,
|
||||
isLoading,
|
||||
request: fetchJobs,
|
||||
@ -49,13 +55,28 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
useCallback(
|
||||
async () => {
|
||||
const params = parseQueryString(qsConfig, location.search);
|
||||
const [response, actionsResponse] = await Promise.all([
|
||||
const [
|
||||
response,
|
||||
actionsResponse,
|
||||
{
|
||||
data: {
|
||||
actions: {
|
||||
GET: {
|
||||
source: { choices },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
] = await Promise.all([
|
||||
UnifiedJobsAPI.read({ ...params }),
|
||||
UnifiedJobsAPI.readOptions(),
|
||||
InventorySourcesAPI.readOptions(),
|
||||
]);
|
||||
|
||||
return {
|
||||
results: response.data.results,
|
||||
count: response.data.count,
|
||||
inventorySourceChoices: choices,
|
||||
relatedSearchableKeys: (
|
||||
actionsResponse?.data?.related_search_fields || []
|
||||
).map((val) => val.slice(0, -8)),
|
||||
@ -69,6 +90,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
{
|
||||
results: [],
|
||||
count: 0,
|
||||
inventorySourceChoices: [],
|
||||
relatedSearchableKeys: [],
|
||||
searchableKeys: [],
|
||||
}
|
||||
@ -264,6 +286,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
renderRow={(job, index) => (
|
||||
<JobListItem
|
||||
key={job.id}
|
||||
inventorySourceLabels={inventorySourceChoices}
|
||||
job={job}
|
||||
isExpanded={expanded.some((row) => row.id === job.id)}
|
||||
onExpand={() => handleExpand(job)}
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
SystemJobsAPI,
|
||||
UnifiedJobsAPI,
|
||||
WorkflowJobsAPI,
|
||||
InventorySourcesAPI,
|
||||
} from 'api';
|
||||
import {
|
||||
mountWithContexts,
|
||||
@ -140,6 +141,20 @@ describe('<JobList />', () => {
|
||||
related_search_fields: [],
|
||||
},
|
||||
});
|
||||
InventorySourcesAPI.readOptions.mockResolvedValue({
|
||||
data: {
|
||||
actions: {
|
||||
GET: {
|
||||
source: {
|
||||
choices: [
|
||||
['scm', 'Sourced from Project'],
|
||||
['file', 'File, Directory or Script'],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
debug = global.console.debug; // eslint-disable-line prefer-destructuring
|
||||
global.console.debug = () => {};
|
||||
});
|
||||
|
||||
@ -28,6 +28,7 @@ function JobListItem({
|
||||
onSelect,
|
||||
showTypeColumn = false,
|
||||
isSuperUser = false,
|
||||
inventorySourceLabels,
|
||||
}) {
|
||||
const labelId = `check-action-${job.id}`;
|
||||
|
||||
@ -151,6 +152,16 @@ function JobListItem({
|
||||
<Td colSpan={showTypeColumn ? 6 : 5}>
|
||||
<ExpandableRowContent>
|
||||
<DetailList>
|
||||
{job.type === 'inventory_update' &&
|
||||
inventorySourceLabels.length > 0 && (
|
||||
<Detail
|
||||
dataCy="job-inventory-source-type"
|
||||
label={t`Source`}
|
||||
value={inventorySourceLabels.map(([string, label]) =>
|
||||
string === job.source ? label : null
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<LaunchedByDetail job={job} />
|
||||
{job_template && (
|
||||
<Detail
|
||||
|
||||
@ -61,6 +61,34 @@ describe('<JobListItem />', () => {
|
||||
expect(wrapper.find('LaunchButton').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should render souce data in expanded view', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<table>
|
||||
<tbody>
|
||||
<JobListItem
|
||||
isExpanded
|
||||
inventorySourceLabels={[
|
||||
['scm', 'Sourced from Project'],
|
||||
['file', 'File, Directory or Script'],
|
||||
]}
|
||||
job={{
|
||||
...mockJob,
|
||||
type: 'inventory_update',
|
||||
source: 'scm',
|
||||
summary_fields: { user_capabilities: { start: false } },
|
||||
}}
|
||||
detailUrl={`/jobs/playbook/${mockJob.id}`}
|
||||
onSelect={() => {}}
|
||||
isSelected={false}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(wrapper.find('ExpandableRowContent')).toHaveLength(1);
|
||||
expect(
|
||||
wrapper.find('dd[data-cy="job-inventory-source-type-value"]').text()
|
||||
).toBe('Sourced from Project');
|
||||
});
|
||||
test('launch button hidden from users without launch capabilities', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<table>
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
import { t } from '@lingui/macro';
|
||||
import { CaretLeftIcon } from '@patternfly/react-icons';
|
||||
import { Card, PageSection } from '@patternfly/react-core';
|
||||
import { InventorySourcesAPI } from 'api';
|
||||
import ContentError from 'components/ContentError';
|
||||
import ContentLoading from 'components/ContentLoading';
|
||||
import RoutedTabs from 'components/RoutedTabs';
|
||||
@ -41,7 +42,12 @@ function Job({ setBreadcrumb }) {
|
||||
isLoading,
|
||||
error,
|
||||
request: fetchJob,
|
||||
result: { jobDetail, eventRelatedSearchableKeys, eventSearchableKeys },
|
||||
result: {
|
||||
jobDetail,
|
||||
eventRelatedSearchableKeys,
|
||||
eventSearchableKeys,
|
||||
inventorySourceChoices,
|
||||
},
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
let eventOptions = {};
|
||||
@ -63,9 +69,16 @@ function Job({ setBreadcrumb }) {
|
||||
|
||||
jobDetailData.summary_fields.credentials = results;
|
||||
}
|
||||
|
||||
setBreadcrumb(jobDetailData);
|
||||
let choices;
|
||||
if (jobDetailData.type === 'inventory_update') {
|
||||
choices = await InventorySourcesAPI.readOptions();
|
||||
}
|
||||
|
||||
return {
|
||||
inventorySourceChoices:
|
||||
choices?.data?.actions?.GET?.source?.choices || [],
|
||||
jobDetail: jobDetailData,
|
||||
eventRelatedSearchableKeys: (
|
||||
eventOptions?.related_search_fields || []
|
||||
@ -77,6 +90,7 @@ function Job({ setBreadcrumb }) {
|
||||
}, [id, type, setBreadcrumb]),
|
||||
{
|
||||
jobDetail: null,
|
||||
inventorySourceChoices: [],
|
||||
eventRelatedSearchableKeys: [],
|
||||
eventSearchableKeys: [],
|
||||
}
|
||||
@ -145,7 +159,10 @@ function Job({ setBreadcrumb }) {
|
||||
key={job.type === 'workflow_job' ? 'workflow-details' : 'details'}
|
||||
path="/jobs/:typeSegment/:id/details"
|
||||
>
|
||||
<JobDetail job={job} />
|
||||
<JobDetail
|
||||
job={job}
|
||||
inventorySourceLabels={inventorySourceChoices}
|
||||
/>
|
||||
</Route>,
|
||||
<Route key="output" path="/jobs/:typeSegment/:id/output">
|
||||
{job.type === 'workflow_job' ? (
|
||||
|
||||
@ -50,7 +50,7 @@ const VERBOSITY = {
|
||||
4: '4 (Connection Debug)',
|
||||
};
|
||||
|
||||
function JobDetail({ job }) {
|
||||
function JobDetail({ job, inventorySourceLabels }) {
|
||||
const { me } = useConfig();
|
||||
const {
|
||||
created_by,
|
||||
@ -203,17 +203,28 @@ function JobDetail({ job }) {
|
||||
/>
|
||||
)}
|
||||
{inventory_source && (
|
||||
<Detail
|
||||
dataCy="job-inventory-source"
|
||||
label={t`Inventory Source`}
|
||||
value={
|
||||
<Link
|
||||
to={`/inventories/inventory/${inventory.id}/sources/${inventory_source.id}`}
|
||||
>
|
||||
{inventory_source.name}
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<Detail
|
||||
dataCy="job-inventory-source"
|
||||
label={t`Inventory Source`}
|
||||
value={
|
||||
<Link
|
||||
to={`/inventories/inventory/${inventory.id}/sources/${inventory_source.id}`}
|
||||
>
|
||||
{inventory_source.name}
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
{inventorySourceLabels.length > 0 && (
|
||||
<Detail
|
||||
dataCy="job-inventory-source-type"
|
||||
label={t`Source`}
|
||||
value={inventorySourceLabels.map(([string, label]) =>
|
||||
string === job.source ? label : null
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{inventory_source && inventory_source.source === 'scm' && (
|
||||
<Detail
|
||||
|
||||
@ -143,6 +143,42 @@ describe('<JobDetail />', () => {
|
||||
assertDetail('Job Type', 'Run Command');
|
||||
});
|
||||
|
||||
test('should display source data', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<JobDetail
|
||||
job={{
|
||||
...mockJobData,
|
||||
source: 'scm',
|
||||
type: 'inventory_update',
|
||||
module_name: 'command',
|
||||
module_args: 'echo hello_world',
|
||||
summary_fields: {
|
||||
...mockJobData.summary_fields,
|
||||
inventory_source: { id: 1, name: 'Inventory Source' },
|
||||
credential: {
|
||||
id: 2,
|
||||
name: 'Machine cred',
|
||||
description: '',
|
||||
kind: 'ssh',
|
||||
cloud: false,
|
||||
kubernetes: false,
|
||||
credential_type_id: 1,
|
||||
},
|
||||
source_workflow_job: {
|
||||
id: 1234,
|
||||
name: 'Test Source Workflow',
|
||||
},
|
||||
},
|
||||
}}
|
||||
inventorySourceLabels={[
|
||||
['scm', 'Sourced from Project'],
|
||||
['file', 'File, Directory or Script'],
|
||||
]}
|
||||
/>
|
||||
);
|
||||
assertDetail('Source', 'Sourced from Project');
|
||||
});
|
||||
|
||||
test('should show schedule that launched workflow job', async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<JobDetail
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user