mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 01:47:35 -02:30
Adds webhooks to jt form
This commit is contained in:
@@ -87,6 +87,10 @@ class JobTemplates extends SchedulesMixin(
|
|||||||
readWebhookKey(id) {
|
readWebhookKey(id) {
|
||||||
return this.http.get(`${this.baseUrl}${id}/webhook_key/`);
|
return this.http.get(`${this.baseUrl}${id}/webhook_key/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateWebhookKey(id) {
|
||||||
|
return this.http.post(`${this.baseUrl}${id}/webhook_key/`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default JobTemplates;
|
export default JobTemplates;
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ export { default as InstanceGroupsLookup } from './InstanceGroupsLookup';
|
|||||||
export { default as InventoryLookup } from './InventoryLookup';
|
export { default as InventoryLookup } from './InventoryLookup';
|
||||||
export { default as ProjectLookup } from './ProjectLookup';
|
export { default as ProjectLookup } from './ProjectLookup';
|
||||||
export { default as MultiCredentialsLookup } from './MultiCredentialsLookup';
|
export { default as MultiCredentialsLookup } from './MultiCredentialsLookup';
|
||||||
|
export { default as CredentialLookup } from './CredentialLookup';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useHistory } from 'react-router-dom';
|
|||||||
import { Card, PageSection } from '@patternfly/react-core';
|
import { Card, PageSection } from '@patternfly/react-core';
|
||||||
import { CardBody } from '@components/Card';
|
import { CardBody } from '@components/Card';
|
||||||
import JobTemplateForm from '../shared/JobTemplateForm';
|
import JobTemplateForm from '../shared/JobTemplateForm';
|
||||||
import { JobTemplatesAPI } from '@api';
|
import { JobTemplatesAPI, OrganizationsAPI } from '@api';
|
||||||
|
|
||||||
function JobTemplateAdd() {
|
function JobTemplateAdd() {
|
||||||
const [formSubmitError, setFormSubmitError] = useState(null);
|
const [formSubmitError, setFormSubmitError] = useState(null);
|
||||||
@@ -15,11 +15,13 @@ function JobTemplateAdd() {
|
|||||||
instanceGroups,
|
instanceGroups,
|
||||||
initialInstanceGroups,
|
initialInstanceGroups,
|
||||||
credentials,
|
credentials,
|
||||||
|
webhook_credential,
|
||||||
...remainingValues
|
...remainingValues
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
setFormSubmitError(null);
|
setFormSubmitError(null);
|
||||||
remainingValues.project = remainingValues.project.id;
|
remainingValues.project = remainingValues.project.id;
|
||||||
|
remainingValues.webhook_credential = webhook_credential?.id;
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data: { id, type },
|
data: { id, type },
|
||||||
@@ -36,6 +38,16 @@ function JobTemplateAdd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function submitLabels(templateId, labels = [], orgId) {
|
async function submitLabels(templateId, labels = [], orgId) {
|
||||||
|
if (!orgId) {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { results },
|
||||||
|
} = await OrganizationsAPI.read();
|
||||||
|
orgId = results[0].id;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
const associationPromises = labels.map(label =>
|
const associationPromises = labels.map(label =>
|
||||||
JobTemplatesAPI.associateLabel(templateId, label, orgId)
|
JobTemplatesAPI.associateLabel(templateId, label, orgId)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
project: 2,
|
project: 2,
|
||||||
playbook: 'Baz',
|
playbook: 'Baz',
|
||||||
inventory: 2,
|
inventory: 2,
|
||||||
|
webhook_credential: undefined,
|
||||||
|
webhook_key: '',
|
||||||
|
webhook_service: '',
|
||||||
|
webhook_url: '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -100,11 +100,13 @@ class JobTemplateEdit extends Component {
|
|||||||
instanceGroups,
|
instanceGroups,
|
||||||
initialInstanceGroups,
|
initialInstanceGroups,
|
||||||
credentials,
|
credentials,
|
||||||
|
webhook_credential,
|
||||||
...remainingValues
|
...remainingValues
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
this.setState({ formSubmitError: null });
|
this.setState({ formSubmitError: null });
|
||||||
remainingValues.project = values.project.id;
|
remainingValues.project = values.project.id;
|
||||||
|
remainingValues.webhook_credential = webhook_credential?.id || null;
|
||||||
try {
|
try {
|
||||||
await JobTemplatesAPI.update(template.id, remainingValues);
|
await JobTemplatesAPI.update(template.id, remainingValues);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ const mockJobTemplate = {
|
|||||||
type: 'job_template',
|
type: 'job_template',
|
||||||
use_fact_cache: false,
|
use_fact_cache: false,
|
||||||
verbosity: '0',
|
verbosity: '0',
|
||||||
|
webhook_credential: null,
|
||||||
|
webhook_key: 'webhook Key',
|
||||||
|
webhook_service: 'gitlab',
|
||||||
|
related: {
|
||||||
|
webhook_receiver: '/api/v2/workflow_job_templates/57/gitlab/',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockRelatedCredentials = {
|
const mockRelatedCredentials = {
|
||||||
@@ -245,6 +251,8 @@ describe('<JobTemplateEdit />', () => {
|
|||||||
delete expected.summary_fields;
|
delete expected.summary_fields;
|
||||||
delete expected.id;
|
delete expected.id;
|
||||||
delete expected.type;
|
delete expected.type;
|
||||||
|
delete expected.related;
|
||||||
|
expected.webhook_url = `${window.location.origin}${mockJobTemplate.related.webhook_receiver}`;
|
||||||
expect(JobTemplatesAPI.update).toHaveBeenCalledWith(1, expected);
|
expect(JobTemplatesAPI.update).toHaveBeenCalledWith(1, expected);
|
||||||
expect(JobTemplatesAPI.disassociateLabel).toHaveBeenCalledTimes(2);
|
expect(JobTemplatesAPI.disassociateLabel).toHaveBeenCalledTimes(2);
|
||||||
expect(JobTemplatesAPI.associateLabel).toHaveBeenCalledTimes(4);
|
expect(JobTemplatesAPI.associateLabel).toHaveBeenCalledTimes(4);
|
||||||
@@ -308,6 +316,12 @@ describe('<JobTemplateEdit />', () => {
|
|||||||
{ id: 1, kind: 'cloud', name: 'Foo' },
|
{ id: 1, kind: 'cloud', name: 'Foo' },
|
||||||
{ id: 2, kind: 'ssh', name: 'Bar' },
|
{ id: 2, kind: 'ssh', name: 'Bar' },
|
||||||
],
|
],
|
||||||
|
webhook_credential: {
|
||||||
|
id: 7,
|
||||||
|
name: 'webhook credential',
|
||||||
|
kind: 'github_token',
|
||||||
|
credential_type_id: 12,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await act(async () =>
|
await act(async () =>
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ function Template({ i18n, me, setBreadcrumb }) {
|
|||||||
role_level: 'notification_admin_role',
|
role_level: 'notification_admin_role',
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
if (data?.related?.webhook_key) {
|
||||||
|
const {
|
||||||
|
data: { webhook_key },
|
||||||
|
} = await JobTemplatesAPI.readWebhookKey(templateId);
|
||||||
|
data.webhook_key = webhook_key;
|
||||||
|
}
|
||||||
setBreadcrumb(data);
|
setBreadcrumb(data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ import {
|
|||||||
import { JobTemplatesAPI, ProjectsAPI } from '@api';
|
import { JobTemplatesAPI, ProjectsAPI } from '@api';
|
||||||
import LabelSelect from './LabelSelect';
|
import LabelSelect from './LabelSelect';
|
||||||
import PlaybookSelect from './PlaybookSelect';
|
import PlaybookSelect from './PlaybookSelect';
|
||||||
|
import WebhookSubForm from './WebhooksSubForm';
|
||||||
|
|
||||||
|
const { origin } = document.location;
|
||||||
|
|
||||||
function JobTemplateForm({
|
function JobTemplateForm({
|
||||||
template,
|
template,
|
||||||
@@ -59,6 +62,10 @@ function JobTemplateForm({
|
|||||||
Boolean(template?.host_config_key)
|
Boolean(template?.host_config_key)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [enableWebhooks, setEnableWebhooks] = useState(
|
||||||
|
Boolean(template.webhook_service)
|
||||||
|
);
|
||||||
|
|
||||||
const { values: formikValues } = useFormikContext();
|
const { values: formikValues } = useFormikContext();
|
||||||
const [jobTypeField, jobTypeMeta, jobTypeHelpers] = useField({
|
const [jobTypeField, jobTypeMeta, jobTypeHelpers] = useField({
|
||||||
name: 'job_type',
|
name: 'job_type',
|
||||||
@@ -87,6 +94,10 @@ function JobTemplateForm({
|
|||||||
);
|
);
|
||||||
const [jobTagsField, , jobTagsHelpers] = useField('job_tags');
|
const [jobTagsField, , jobTagsHelpers] = useField('job_tags');
|
||||||
const [skipTagsField, , skipTagsHelpers] = useField('skip_tags');
|
const [skipTagsField, , skipTagsHelpers] = useField('skip_tags');
|
||||||
|
const webhookService = useField('webhook_service');
|
||||||
|
const webhookUrl = useField('webhook_url');
|
||||||
|
const webhookKey = useField('webhook_key');
|
||||||
|
const webhookCredential = useField('webhook_credential');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
request: fetchProject,
|
request: fetchProject,
|
||||||
@@ -174,16 +185,23 @@ function JobTemplateForm({
|
|||||||
];
|
];
|
||||||
let callbackUrl;
|
let callbackUrl;
|
||||||
if (template?.related) {
|
if (template?.related) {
|
||||||
const { origin } = document.location;
|
|
||||||
const path = template.related.callback || `${template.url}callback`;
|
const path = template.related.callback || `${template.url}callback`;
|
||||||
callbackUrl = `${origin}${path}`;
|
callbackUrl = `${origin}${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instanceGroupLoading || hasProjectLoading) {
|
if (
|
||||||
|
instanceGroupLoading ||
|
||||||
|
hasProjectLoading
|
||||||
|
// credentialContentLoading
|
||||||
|
) {
|
||||||
return <ContentLoading />;
|
return <ContentLoading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instanceGroupError || projectContentError) {
|
if (
|
||||||
|
instanceGroupError ||
|
||||||
|
projectContentError
|
||||||
|
// credentialContentError
|
||||||
|
) {
|
||||||
return <ContentError error={contentError} />;
|
return <ContentError error={contentError} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,6 +516,39 @@ function JobTemplateForm({
|
|||||||
setAllowCallbacks(checked);
|
setAllowCallbacks(checked);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
aria-label={i18n._(t`Enable Webhook`)}
|
||||||
|
label={
|
||||||
|
<span>
|
||||||
|
{i18n._(t`Enable Webhook`)}
|
||||||
|
|
||||||
|
<FieldTooltip
|
||||||
|
content={i18n._(
|
||||||
|
t`Enable webhook for this workflow job template.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
id="wfjt-enabled-webhooks"
|
||||||
|
isChecked={
|
||||||
|
Boolean(webhookService[0].value) || enableWebhooks
|
||||||
|
}
|
||||||
|
onChange={checked => {
|
||||||
|
setEnableWebhooks(checked);
|
||||||
|
webhookService[2].setValue(
|
||||||
|
!checked ? '' : webhookService[1].initialValue
|
||||||
|
);
|
||||||
|
webhookUrl[2].setValue(
|
||||||
|
!checked ? '' : webhookUrl[1].initialValue
|
||||||
|
);
|
||||||
|
webhookKey[2].setValue(
|
||||||
|
!checked ? '' : webhookKey[1].initialValue
|
||||||
|
);
|
||||||
|
webhookCredential[2].setValue(
|
||||||
|
!checked ? null : webhookCredential[1].initialValue
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
id="option-concurrent"
|
id="option-concurrent"
|
||||||
name="allow_simultaneous"
|
name="allow_simultaneous"
|
||||||
@@ -516,6 +567,7 @@ function JobTemplateForm({
|
|||||||
</FormCheckboxLayout>
|
</FormCheckboxLayout>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</FormFullWidthLayout>
|
</FormFullWidthLayout>
|
||||||
|
<WebhookSubForm enableWebhooks={enableWebhooks} />
|
||||||
{allowCallbacks && (
|
{allowCallbacks && (
|
||||||
<>
|
<>
|
||||||
{callbackUrl && (
|
{callbackUrl && (
|
||||||
@@ -616,6 +668,12 @@ const FormikApp = withFormik({
|
|||||||
instanceGroups: [],
|
instanceGroups: [],
|
||||||
credentials: summary_fields.credentials || [],
|
credentials: summary_fields.credentials || [],
|
||||||
extra_vars: template.extra_vars || '---\n',
|
extra_vars: template.extra_vars || '---\n',
|
||||||
|
webhook_service: template.webhook_service || '',
|
||||||
|
webhook_url: template?.related?.webhook_receiver
|
||||||
|
? `${origin}${template.related.webhook_receiver}`
|
||||||
|
: '',
|
||||||
|
webhook_key: template.webhook_key || '',
|
||||||
|
webhook_credential: template?.summary_fields?.webhook_credential || null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleSubmit: async (values, { props, setErrors }) => {
|
handleSubmit: async (values, { props, setErrors }) => {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React from 'react';
|
|||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
import { sleep } from '@testUtils/testUtils';
|
import { sleep } from '@testUtils/testUtils';
|
||||||
|
import { Route } from 'react-router-dom';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import JobTemplateForm from './JobTemplateForm';
|
import JobTemplateForm from './JobTemplateForm';
|
||||||
import { LabelsAPI, JobTemplatesAPI, ProjectsAPI, CredentialsAPI } from '@api';
|
import { LabelsAPI, JobTemplatesAPI, ProjectsAPI, CredentialsAPI } from '@api';
|
||||||
|
|
||||||
@@ -34,6 +36,10 @@ describe('<JobTemplateForm />', () => {
|
|||||||
{ id: 2, kind: 'ssh', name: 'Bar' },
|
{ id: 2, kind: 'ssh', name: 'Bar' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
related: { webhook_receiver: '/api/v2/workflow_job_templates/57/gitlab/' },
|
||||||
|
webhook_key: 'webhook key',
|
||||||
|
webhook_service: 'github',
|
||||||
|
webhook_credential: 7,
|
||||||
};
|
};
|
||||||
const mockInstanceGroups = [
|
const mockInstanceGroups = [
|
||||||
{
|
{
|
||||||
@@ -86,6 +92,9 @@ describe('<JobTemplateForm />', () => {
|
|||||||
JobTemplatesAPI.readInstanceGroups.mockReturnValue({
|
JobTemplatesAPI.readInstanceGroups.mockReturnValue({
|
||||||
data: { results: mockInstanceGroups },
|
data: { results: mockInstanceGroups },
|
||||||
});
|
});
|
||||||
|
JobTemplatesAPI.updateWebhookKey.mockReturnValue({
|
||||||
|
data: { webhook_key: 'webhook key' },
|
||||||
|
});
|
||||||
ProjectsAPI.readPlaybooks.mockReturnValue({
|
ProjectsAPI.readPlaybooks.mockReturnValue({
|
||||||
data: ['debug.yml'],
|
data: ['debug.yml'],
|
||||||
});
|
});
|
||||||
@@ -209,6 +218,80 @@ describe('<JobTemplateForm />', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('webhooks and enable concurrent jobs functions properly', async () => {
|
||||||
|
let wrapper;
|
||||||
|
const history = createMemoryHistory({
|
||||||
|
initialEntries: ['/templates/job_template/1/edit'],
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<Route
|
||||||
|
path="/templates/job_template/:id/edit"
|
||||||
|
component={() => (
|
||||||
|
<JobTemplateForm
|
||||||
|
template={mockData}
|
||||||
|
handleSubmit={jest.fn()}
|
||||||
|
handleCancel={jest.fn()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
router: {
|
||||||
|
history,
|
||||||
|
route: {
|
||||||
|
location: history.location,
|
||||||
|
match: { params: { id: 1 } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
act(() => {
|
||||||
|
wrapper.find('Checkbox[aria-label="Enable Webhook"]').invoke('onChange')(
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
currentTarget: { value: true, type: 'change', checked: true },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(
|
||||||
|
wrapper.find('Checkbox[aria-label="Enable Webhook"]').prop('isChecked')
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
wrapper.find('input[aria-label="wfjt-webhook-key"]').prop('readOnly')
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
wrapper.find('input[aria-label="wfjt-webhook-key"]').prop('value')
|
||||||
|
).toBe('webhook key');
|
||||||
|
await act(() =>
|
||||||
|
wrapper.find('Button[aria-label="Update webhook key"]').prop('onClick')()
|
||||||
|
);
|
||||||
|
expect(JobTemplatesAPI.updateWebhookKey).toBeCalledWith('1');
|
||||||
|
expect(
|
||||||
|
wrapper.find('TextInputBase[aria-label="Webhook URL"]').prop('value')
|
||||||
|
).toContain('/api/v2/workflow_job_templates/57/gitlab/');
|
||||||
|
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
|
expect(wrapper.find('FormGroup[name="webhook_service"]').length).toBe(1);
|
||||||
|
|
||||||
|
await act(async () =>
|
||||||
|
wrapper.find('AnsibleSelect#webhook_service').prop('onChange')(
|
||||||
|
{},
|
||||||
|
'gitlab'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
|
expect(wrapper.find('AnsibleSelect#webhook_service').prop('value')).toBe(
|
||||||
|
'gitlab'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('should call handleSubmit when Submit button is clicked', async () => {
|
test('should call handleSubmit when Submit button is clicked', async () => {
|
||||||
const handleSubmit = jest.fn();
|
const handleSubmit = jest.fn();
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|||||||
207
awx/ui_next/src/screens/Template/shared/WebhooksSubForm.jsx
Normal file
207
awx/ui_next/src/screens/Template/shared/WebhooksSubForm.jsx
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
import React, { useEffect, useCallback, useState } from 'react';
|
||||||
|
import { SyncAltIcon } from '@patternfly/react-icons';
|
||||||
|
import { useParams, useRouteMatch } from 'react-router-dom';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
TextInput,
|
||||||
|
InputGroup,
|
||||||
|
Button,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
import ContentError from '@components/ContentError';
|
||||||
|
import ContentLoading from '@components/ContentLoading';
|
||||||
|
import useRequest from '@util/useRequest';
|
||||||
|
import { useField } from 'formik';
|
||||||
|
import { FormColumnLayout } from '@components/FormLayout';
|
||||||
|
import { CredentialLookup } from '@components/Lookup';
|
||||||
|
import AnsibleSelect from '@components/AnsibleSelect';
|
||||||
|
import { FieldTooltip } from '@components/FormField';
|
||||||
|
import { JobTemplatesAPI, CredentialTypesAPI } from '@api';
|
||||||
|
|
||||||
|
function WebhookSubForm({ i18n, enableWebhooks }) {
|
||||||
|
const [contentError, setContentError] = useState(null);
|
||||||
|
const jtAddMatch = useRouteMatch('/templates/job_template/add');
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
|
const { origin } = document.location;
|
||||||
|
|
||||||
|
const [
|
||||||
|
webhookServiceField,
|
||||||
|
webhookServiceMeta,
|
||||||
|
webhookServiceHelpers,
|
||||||
|
] = useField('webhook_service');
|
||||||
|
|
||||||
|
const [webhookUrlField, , webhookUrlHelpers] = useField('webhook_url');
|
||||||
|
const [webhookKeyField, webhookKeyMeta, webhookKeyHelpers] = useField(
|
||||||
|
'webhook_key'
|
||||||
|
);
|
||||||
|
const [
|
||||||
|
webhookCredentialField,
|
||||||
|
webhookCredentialMeta,
|
||||||
|
webhookCredentialHelpers,
|
||||||
|
] = useField('webhook_credential');
|
||||||
|
|
||||||
|
const {
|
||||||
|
request: loadCredentialType,
|
||||||
|
error,
|
||||||
|
isLoading,
|
||||||
|
result: credTypeId,
|
||||||
|
} = useRequest(
|
||||||
|
useCallback(async () => {
|
||||||
|
let results;
|
||||||
|
if (webhookServiceField.value) {
|
||||||
|
results = await CredentialTypesAPI.read({
|
||||||
|
namespace: `${webhookServiceField.value}_token`,
|
||||||
|
});
|
||||||
|
// TODO: Consider how to handle the situation where the results returns
|
||||||
|
// and empty array, or any of the other values is undefined or null (data, results, id)
|
||||||
|
}
|
||||||
|
return results?.data?.results[0]?.id;
|
||||||
|
}, [webhookServiceField.value])
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadCredentialType();
|
||||||
|
}, [loadCredentialType]);
|
||||||
|
|
||||||
|
const changeWebhookKey = async () => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { webhook_key: key },
|
||||||
|
} = await JobTemplatesAPI.updateWebhookKey(id);
|
||||||
|
webhookKeyHelpers.setValue(key);
|
||||||
|
} catch (err) {
|
||||||
|
setContentError(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const webhookServiceOptions = [
|
||||||
|
{
|
||||||
|
value: '',
|
||||||
|
key: '',
|
||||||
|
label: i18n._(t`Choose a Webhook Service`),
|
||||||
|
isDisabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'github',
|
||||||
|
key: 'github',
|
||||||
|
label: i18n._(t`GitHub`),
|
||||||
|
isDisabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'gitlab',
|
||||||
|
key: 'gitlab',
|
||||||
|
label: i18n._(t`GitLab`),
|
||||||
|
isDisabled: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (error || contentError) {
|
||||||
|
return <ContentError error={error} />;
|
||||||
|
}
|
||||||
|
if (isLoading) {
|
||||||
|
return <ContentLoading />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
enableWebhooks && (
|
||||||
|
<FormColumnLayout>
|
||||||
|
<FormGroup
|
||||||
|
name="webhook_service"
|
||||||
|
fieldId="webhook_service"
|
||||||
|
helperTextInvalid={webhookServiceMeta.error}
|
||||||
|
label={i18n._(t`Webhook Service`)}
|
||||||
|
>
|
||||||
|
<FieldTooltip content={i18n._(t`Select a webhook service.`)} />
|
||||||
|
<AnsibleSelect
|
||||||
|
{...webhookServiceField}
|
||||||
|
id="webhook_service"
|
||||||
|
data={webhookServiceOptions}
|
||||||
|
onChange={(event, val) => {
|
||||||
|
webhookServiceHelpers.setValue(val);
|
||||||
|
webhookUrlHelpers.setValue(
|
||||||
|
`${origin}/api/v2/job_templates/${id}/${val}/`
|
||||||
|
);
|
||||||
|
if (val === webhookServiceMeta.initialValue || val === '') {
|
||||||
|
webhookKeyHelpers.setValue(webhookKeyMeta.initialValue);
|
||||||
|
webhookCredentialHelpers.setValue(
|
||||||
|
webhookCredentialMeta.initialValue
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
webhookKeyHelpers.setValue(
|
||||||
|
i18n
|
||||||
|
._(t`a new webhook key will be generated on save.`)
|
||||||
|
.toUpperCase()
|
||||||
|
);
|
||||||
|
webhookCredentialHelpers.setValue(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
{!jtAddMatch && (
|
||||||
|
<>
|
||||||
|
<FormGroup
|
||||||
|
type="text"
|
||||||
|
fieldId="jt-webhookURL"
|
||||||
|
label={i18n._(t`Webhook URL`)}
|
||||||
|
name="webhook_url"
|
||||||
|
>
|
||||||
|
<FieldTooltip
|
||||||
|
content={i18n._(
|
||||||
|
t`Webhook services can launch jobs with this workflow job template by making a POST request to this URL.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
id="t-webhookURL"
|
||||||
|
aria-label={i18n._(t`Webhook URL`)}
|
||||||
|
value={webhookUrlField.value}
|
||||||
|
isReadOnly
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label={i18n._(t`Webhook Key`)}
|
||||||
|
fieldId="template-webhook_key"
|
||||||
|
>
|
||||||
|
<FieldTooltip
|
||||||
|
content={i18n._(
|
||||||
|
t`Webhook services can use this as a shared secret.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<InputGroup>
|
||||||
|
<TextInput
|
||||||
|
id="template-webhook_key"
|
||||||
|
isReadOnly
|
||||||
|
aria-label="wfjt-webhook-key"
|
||||||
|
value={webhookKeyField.value}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="tertiary"
|
||||||
|
aria-label={i18n._(t`Update webhook key`)}
|
||||||
|
onClick={changeWebhookKey}
|
||||||
|
>
|
||||||
|
<SyncAltIcon />
|
||||||
|
</Button>
|
||||||
|
</InputGroup>
|
||||||
|
</FormGroup>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{credTypeId && (
|
||||||
|
<CredentialLookup
|
||||||
|
label={i18n._(t`Webhook Credential`)}
|
||||||
|
tooltip={i18n._(
|
||||||
|
t`Optionally select the credential to use to send status updates back to the webhook service.`
|
||||||
|
)}
|
||||||
|
credentialTypeId={credTypeId}
|
||||||
|
onChange={value => {
|
||||||
|
webhookCredentialHelpers.setValue(value || null);
|
||||||
|
}}
|
||||||
|
isValid={!webhookCredentialMeta.error}
|
||||||
|
helperTextInvalid={webhookCredentialMeta.error}
|
||||||
|
value={webhookCredentialField.value}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</FormColumnLayout>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default withI18n()(WebhookSubForm);
|
||||||
Reference in New Issue
Block a user