mirror of
https://github.com/ansible/awx.git
synced 2026-01-24 07:51:23 -03:30
Add WF details to workflow node view
This commit is contained in:
parent
f5d4f7858a
commit
4551859248
@ -16,6 +16,7 @@ class JobTemplates extends SchedulesMixin(
|
||||
this.disassociateLabel = this.disassociateLabel.bind(this);
|
||||
this.readCredentials = this.readCredentials.bind(this);
|
||||
this.readAccessList = this.readAccessList.bind(this);
|
||||
this.readWebhookKey = this.readWebhookKey.bind(this);
|
||||
}
|
||||
|
||||
launch(id, data) {
|
||||
@ -82,6 +83,10 @@ class JobTemplates extends SchedulesMixin(
|
||||
destroySurvey(id) {
|
||||
return this.http.delete(`${this.baseUrl}${id}/survey_spec/`);
|
||||
}
|
||||
|
||||
readWebhookKey(id) {
|
||||
return this.http.get(`${this.baseUrl}${id}/webhook_key/`);
|
||||
}
|
||||
}
|
||||
|
||||
export default JobTemplates;
|
||||
|
||||
@ -1,8 +1,120 @@
|
||||
import React from 'react';
|
||||
import { CardBody } from '@components/Card';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
function PromptWFJobTemplateDetail() {
|
||||
return <CardBody>Coming soon :)</CardBody>;
|
||||
import { Chip, ChipGroup, List, ListItem } from '@patternfly/react-core';
|
||||
import CredentialChip from '@components/CredentialChip';
|
||||
import { Detail } from '@components/DetailList';
|
||||
import { VariablesDetail } from '@components/CodeMirrorInput';
|
||||
import Sparkline from '@components/Sparkline';
|
||||
import { toTitleCase } from '@util/strings';
|
||||
|
||||
function PromptWFJobTemplateDetail({ i18n, resource }) {
|
||||
const {
|
||||
allow_simultaneous,
|
||||
extra_vars,
|
||||
limit,
|
||||
related,
|
||||
scm_branch,
|
||||
summary_fields,
|
||||
webhook_key,
|
||||
webhook_service,
|
||||
} = resource;
|
||||
|
||||
let optionsList = '';
|
||||
if (allow_simultaneous || webhook_service) {
|
||||
optionsList = (
|
||||
<List>
|
||||
{allow_simultaneous && (
|
||||
<ListItem>{i18n._(t`Enable Concurrent Jobs`)}</ListItem>
|
||||
)}
|
||||
{webhook_service && <ListItem>{i18n._(t`Enable Webhooks`)}</ListItem>}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
||||
const inventoryKind =
|
||||
summary_fields?.inventory?.kind === 'smart'
|
||||
? 'smart_inventory'
|
||||
: 'inventory';
|
||||
|
||||
const recentJobs = summary_fields.recent_jobs.map(job => ({
|
||||
...job,
|
||||
type: 'job',
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
{summary_fields.recent_jobs?.length > 0 && (
|
||||
<Detail
|
||||
value={<Sparkline jobs={recentJobs} />}
|
||||
label={i18n._(t`Activity`)}
|
||||
/>
|
||||
)}
|
||||
{summary_fields?.inventory && (
|
||||
<Detail
|
||||
label={i18n._(t`Inventory`)}
|
||||
value={
|
||||
<Link
|
||||
to={`/${inventoryKind}/${summary_fields.inventory?.id}/details`}
|
||||
>
|
||||
{summary_fields.inventory?.name}
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Detail label={i18n._(t`Source Control Branch`)} value={scm_branch} />
|
||||
<Detail label={i18n._(t`Limit`)} value={limit} />
|
||||
<Detail
|
||||
label={i18n._(t`Webhook Service`)}
|
||||
value={toTitleCase(webhook_service)}
|
||||
/>
|
||||
<Detail label={i18n._(t`Webhook Key`)} value={webhook_key} />
|
||||
{related.webhook_receiver && (
|
||||
<Detail
|
||||
label={i18n._(t`Webhook URL`)}
|
||||
value={`${window.location.origin}${related.webhook_receiver}`}
|
||||
/>
|
||||
)}
|
||||
{optionsList && <Detail label={i18n._(t`Options`)} value={optionsList} />}
|
||||
{summary_fields?.webhook_credential && (
|
||||
<Detail
|
||||
fullWidth
|
||||
label={i18n._(t`Webhook Credential`)}
|
||||
value={
|
||||
<CredentialChip
|
||||
key={summary_fields.webhook_credential?.id}
|
||||
credential={summary_fields.webhook_credential}
|
||||
isReadOnly
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{summary_fields?.labels?.results?.length > 0 && (
|
||||
<Detail
|
||||
fullWidth
|
||||
label={i18n._(t`Labels`)}
|
||||
value={
|
||||
<ChipGroup numChips={5}>
|
||||
{summary_fields.labels.results.map(label => (
|
||||
<Chip key={label.id} isReadOnly>
|
||||
{label.name}
|
||||
</Chip>
|
||||
))}
|
||||
</ChipGroup>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{extra_vars && (
|
||||
<VariablesDetail
|
||||
label={i18n._(t`Variables`)}
|
||||
rows={4}
|
||||
value={extra_vars}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default PromptWFJobTemplateDetail;
|
||||
export default withI18n()(PromptWFJobTemplateDetail);
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import PromptWFJobTemplateDetail from './PromptWFJobTemplateDetail';
|
||||
import mockData from './data.workflow_template.json';
|
||||
|
||||
const mockWF = {
|
||||
...mockData,
|
||||
webhook_key: 'Pim3mRXT0',
|
||||
};
|
||||
|
||||
describe('PromptWFJobTemplateDetail', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
wrapper = mountWithContexts(
|
||||
<PromptWFJobTemplateDetail resource={mockWF} />
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('should render successfully', () => {
|
||||
expect(wrapper.find('PromptWFJobTemplateDetail')).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should render expected details', () => {
|
||||
function assertDetail(label, value) {
|
||||
expect(wrapper.find(`Detail[label="${label}"] dt`).text()).toBe(label);
|
||||
expect(wrapper.find(`Detail[label="${label}"] dd`).text()).toBe(value);
|
||||
}
|
||||
|
||||
expect(wrapper.find('StatusIcon')).toHaveLength(1);
|
||||
assertDetail('Inventory', 'Mock Smart Inv');
|
||||
assertDetail('Source Control Branch', '/bar/');
|
||||
assertDetail('Limit', 'hosts1,hosts2');
|
||||
assertDetail('Webhook Service', 'Github');
|
||||
assertDetail('Webhook Key', 'Pim3mRXT0');
|
||||
expect(wrapper.find('Detail[label="Webhook URL"] dd').text()).toEqual(
|
||||
expect.stringContaining('/api/v2/workflow_job_templates/47/github/')
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Detail[label="Options"]')
|
||||
.containsAllMatchingElements([
|
||||
<li>Enable Concurrent Jobs</li>,
|
||||
<li>Enable Webhooks</li>,
|
||||
])
|
||||
).toEqual(true);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Detail[label="Webhook Credential"]')
|
||||
.containsAllMatchingElements([
|
||||
<span>
|
||||
<strong>Github Token:</strong>github
|
||||
</span>,
|
||||
])
|
||||
).toEqual(true);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Detail[label="Labels"]')
|
||||
.containsAllMatchingElements([<span>L_10o0</span>, <span>L_20o0</span>])
|
||||
).toEqual(true);
|
||||
expect(wrapper.find('VariablesDetail').prop('value')).toEqual(
|
||||
'---\nmock: data'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,156 @@
|
||||
{
|
||||
"id": 47,
|
||||
"type": "workflow_job_template",
|
||||
"url": "/api/v2/workflow_job_templates/47/",
|
||||
"related": {
|
||||
"created_by": "/api/v2/users/8/",
|
||||
"modified_by": "/api/v2/users/1/",
|
||||
"last_job": "/api/v2/workflow_jobs/226/",
|
||||
"workflow_jobs": "/api/v2/workflow_job_templates/47/workflow_jobs/",
|
||||
"schedules": "/api/v2/workflow_job_templates/47/schedules/",
|
||||
"launch": "/api/v2/workflow_job_templates/47/launch/",
|
||||
"webhook_key": "/api/v2/workflow_job_templates/47/webhook_key/",
|
||||
"webhook_receiver": "/api/v2/workflow_job_templates/47/github/",
|
||||
"workflow_nodes": "/api/v2/workflow_job_templates/47/workflow_nodes/",
|
||||
"labels": "/api/v2/workflow_job_templates/47/labels/",
|
||||
"activity_stream": "/api/v2/workflow_job_templates/47/activity_stream/",
|
||||
"notification_templates_started": "/api/v2/workflow_job_templates/47/notification_templates_started/",
|
||||
"notification_templates_success": "/api/v2/workflow_job_templates/47/notification_templates_success/",
|
||||
"notification_templates_error": "/api/v2/workflow_job_templates/47/notification_templates_error/",
|
||||
"notification_templates_approvals": "/api/v2/workflow_job_templates/47/notification_templates_approvals/",
|
||||
"access_list": "/api/v2/workflow_job_templates/47/access_list/",
|
||||
"object_roles": "/api/v2/workflow_job_templates/47/object_roles/",
|
||||
"survey_spec": "/api/v2/workflow_job_templates/47/survey_spec/",
|
||||
"copy": "/api/v2/workflow_job_templates/47/copy/",
|
||||
"organization": "/api/v2/organizations/3/",
|
||||
"webhook_credential": "/api/v2/credentials/8/"
|
||||
},
|
||||
"summary_fields": {
|
||||
"organization": {
|
||||
"id": 3,
|
||||
"name": "Mock Org",
|
||||
"description": ""
|
||||
},
|
||||
"inventory": {
|
||||
"id": 7,
|
||||
"name": "Mock Smart Inv",
|
||||
"description": "",
|
||||
"has_active_failures": false,
|
||||
"total_hosts": 1,
|
||||
"hosts_with_active_failures": 0,
|
||||
"total_groups": 0,
|
||||
"has_inventory_sources": false,
|
||||
"total_inventory_sources": 0,
|
||||
"inventory_sources_with_failures": 0,
|
||||
"organization_id": 1,
|
||||
"kind": "smart"
|
||||
},
|
||||
"last_job": {
|
||||
"id": 226,
|
||||
"name": "abc",
|
||||
"description": "From Tower bulk-data script",
|
||||
"finished": "2020-04-08T21:30:44.282245Z",
|
||||
"status": "failed",
|
||||
"failed": true
|
||||
},
|
||||
"last_update": {
|
||||
"id": 226,
|
||||
"name": "abc",
|
||||
"description": "From Tower bulk-data script",
|
||||
"status": "failed",
|
||||
"failed": true
|
||||
},
|
||||
"webhook_credential": {
|
||||
"id": 8,
|
||||
"name": "github",
|
||||
"description": "",
|
||||
"kind": "github_token",
|
||||
"cloud": false,
|
||||
"credential_type_id": 12
|
||||
},
|
||||
"created_by": {
|
||||
"id": 8,
|
||||
"username": "user-2",
|
||||
"first_name": "",
|
||||
"last_name": ""
|
||||
},
|
||||
"modified_by": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"first_name": "",
|
||||
"last_name": ""
|
||||
},
|
||||
"object_roles": {
|
||||
"admin_role": {
|
||||
"description": "Can manage all aspects of the workflow job template",
|
||||
"name": "Admin",
|
||||
"id": 260
|
||||
},
|
||||
"execute_role": {
|
||||
"description": "May run the workflow job template",
|
||||
"name": "Execute",
|
||||
"id": 261
|
||||
},
|
||||
"read_role": {
|
||||
"description": "May view settings for the workflow job template",
|
||||
"name": "Read",
|
||||
"id": 262
|
||||
},
|
||||
"approval_role": {
|
||||
"description": "Can approve or deny a workflow approval node",
|
||||
"name": "Approve",
|
||||
"id": 263
|
||||
}
|
||||
},
|
||||
"user_capabilities": {
|
||||
"edit": true,
|
||||
"delete": true,
|
||||
"start": true,
|
||||
"schedule": true,
|
||||
"copy": true
|
||||
},
|
||||
"labels": {
|
||||
"count": 2,
|
||||
"results": [
|
||||
{
|
||||
"id": 104,
|
||||
"name": "L_10o0"
|
||||
},
|
||||
{
|
||||
"id": 105,
|
||||
"name": "L_20o0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"recent_jobs": [
|
||||
{
|
||||
"id": 226,
|
||||
"status": "failed",
|
||||
"finished": "2020-04-08T21:30:44.282245Z",
|
||||
"canceled_on": null,
|
||||
"type": "workflow_job"
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2020-04-07T16:38:02.856877Z",
|
||||
"modified": "2020-04-13T20:53:53.761355Z",
|
||||
"name": "Mock Workflow",
|
||||
"description": "Mock WF Description",
|
||||
"last_job_run": "2020-04-08T21:30:44.282245Z",
|
||||
"last_job_failed": true,
|
||||
"next_job_run": null,
|
||||
"status": "failed",
|
||||
"extra_vars": "---\nmock: data",
|
||||
"organization": 3,
|
||||
"survey_enabled": false,
|
||||
"allow_simultaneous": true,
|
||||
"ask_variables_on_launch": false,
|
||||
"inventory": 7,
|
||||
"limit": "hosts1,hosts2",
|
||||
"scm_branch": "/bar/",
|
||||
"ask_inventory_on_launch": true,
|
||||
"ask_scm_branch_on_launch": true,
|
||||
"ask_limit_on_launch": true,
|
||||
"webhook_service": "github",
|
||||
"webhook_credential": 8
|
||||
}
|
||||
@ -72,6 +72,7 @@ function NodeViewModal({ i18n }) {
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
let { data } = await nodeAPI?.readDetail(unifiedJobTemplate.id);
|
||||
|
||||
if (data?.type === 'job_template') {
|
||||
const {
|
||||
data: { results = [] },
|
||||
@ -79,6 +80,13 @@ function NodeViewModal({ i18n }) {
|
||||
data = Object.assign(data, { instance_groups: results });
|
||||
}
|
||||
|
||||
if (data?.related?.webhook_receiver) {
|
||||
const {
|
||||
data: { webhook_key },
|
||||
} = await nodeAPI?.readWebhookKey(data.id);
|
||||
data = Object.assign(data, { webhook_key });
|
||||
}
|
||||
|
||||
return data;
|
||||
}, [nodeAPI, unifiedJobTemplate.id]),
|
||||
null
|
||||
|
||||
@ -11,9 +11,23 @@ import NodeViewModal from './NodeViewModal';
|
||||
jest.mock('@api/models/JobTemplates');
|
||||
jest.mock('@api/models/WorkflowJobTemplates');
|
||||
WorkflowJobTemplatesAPI.readLaunch.mockResolvedValue({});
|
||||
WorkflowJobTemplatesAPI.readDetail.mockResolvedValue({});
|
||||
WorkflowJobTemplatesAPI.readDetail.mockResolvedValue({
|
||||
data: {
|
||||
id: 1,
|
||||
type: 'workflow_job_template',
|
||||
related: {
|
||||
webhook_receiver: '/api/v2/job_templates/7/gitlab/',
|
||||
},
|
||||
},
|
||||
});
|
||||
WorkflowJobTemplatesAPI.readWebhookKey.mockResolvedValue({
|
||||
data: {
|
||||
webhook_key: 'Pim3mRXT0',
|
||||
},
|
||||
});
|
||||
JobTemplatesAPI.readLaunch.mockResolvedValue({});
|
||||
JobTemplatesAPI.readInstanceGroups.mockResolvedValue({});
|
||||
JobTemplatesAPI.readWebhookKey.mockResolvedValue({});
|
||||
JobTemplatesAPI.readDetail.mockResolvedValue({
|
||||
data: {
|
||||
id: 1,
|
||||
@ -74,6 +88,7 @@ describe('NodeViewModal', () => {
|
||||
expect(JobTemplatesAPI.readDetail).not.toHaveBeenCalled();
|
||||
expect(JobTemplatesAPI.readInstanceGroups).not.toHaveBeenCalled();
|
||||
expect(WorkflowJobTemplatesAPI.readLaunch).toHaveBeenCalledWith(1);
|
||||
expect(WorkflowJobTemplatesAPI.readWebhookKey).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
test('Close button dispatches as expected', () => {
|
||||
@ -125,6 +140,7 @@ describe('NodeViewModal', () => {
|
||||
});
|
||||
waitForLoaded(wrapper);
|
||||
expect(WorkflowJobTemplatesAPI.readLaunch).not.toHaveBeenCalled();
|
||||
expect(JobTemplatesAPI.readWebhookKey).not.toHaveBeenCalledWith();
|
||||
expect(JobTemplatesAPI.readLaunch).toHaveBeenCalledWith(1);
|
||||
expect(JobTemplatesAPI.readDetail).toHaveBeenCalledWith(1);
|
||||
expect(JobTemplatesAPI.readInstanceGroups).toHaveBeenCalledTimes(1);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user