diff --git a/awx/ui_next/src/api/models/WorkflowJobTemplates.js b/awx/ui_next/src/api/models/WorkflowJobTemplates.js
index 922d5cde00..20158334ad 100644
--- a/awx/ui_next/src/api/models/WorkflowJobTemplates.js
+++ b/awx/ui_next/src/api/models/WorkflowJobTemplates.js
@@ -6,6 +6,14 @@ class WorkflowJobTemplates extends Base {
this.baseUrl = '/api/v2/workflow_job_templates/';
}
+ readWebhookKey(id) {
+ return this.http.get(`${this.baseUrl}${id}/webhook_key/`);
+ }
+
+ updateWebhookKey(id) {
+ return this.http.post(`${this.baseUrl}${id}/webhook_key/`);
+ }
+
associateLabel(id, label, orgId) {
return this.http.post(`${this.baseUrl}${id}/labels/`, {
name: label.name,
diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx
index 336a3fa996..5a468bcccb 100644
--- a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx
+++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx
@@ -7,6 +7,7 @@ import styled from 'styled-components';
import { Split, SplitItem } from '@patternfly/react-core';
import { CheckboxField } from '@components/FormField';
import { yamlToJson, jsonToYaml, isJson } from '@util/yaml';
+import { FieldTooltip } from '@components/FormField';
import CodeMirrorInput from './CodeMirrorInput';
import YamlJsonToggle from './YamlJsonToggle';
import { JSON_MODE, YAML_MODE } from './constants';
@@ -20,7 +21,15 @@ const StyledCheckboxField = styled(CheckboxField)`
--pf-c-check__label--FontSize: var(--pf-c-form__label--FontSize);
`;
-function VariablesField({ i18n, id, name, label, readOnly, promptId }) {
+function VariablesField({
+ i18n,
+ id,
+ name,
+ label,
+ readOnly,
+ promptId,
+ tooltip,
+}) {
const [field, meta, helpers] = useField(name);
const [mode, setMode] = useState(isJson(field.value) ? JSON_MODE : YAML_MODE);
@@ -32,6 +41,7 @@ function VariablesField({ i18n, id, name, label, readOnly, promptId }) {
+ {tooltip && }
+ {tooltip && }
{
history.push(`/templates`);
};
- if (hasContentLoading) {
+ if (hasTemplateLoading) {
return ;
}
return (
@@ -67,6 +71,7 @@ function WorkflowJobTemplateEdit({ template, hasContentLoading }) {
handleSubmit={handleSubmit}
handleCancel={handleCancel}
template={template}
+ webhook_key={webhook_key}
/>
{formSubmitError ? formSubmitError
: ''}
diff --git a/awx/ui_next/src/screens/Template/shared/WorkflowJobTemplateForm.jsx b/awx/ui_next/src/screens/Template/shared/WorkflowJobTemplateForm.jsx
index f30cea96b5..00ee0beb32 100644
--- a/awx/ui_next/src/screens/Template/shared/WorkflowJobTemplateForm.jsx
+++ b/awx/ui_next/src/screens/Template/shared/WorkflowJobTemplateForm.jsx
@@ -1,27 +1,54 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { Formik, Field } from 'formik';
-
-import { Form, FormGroup } from '@patternfly/react-core';
+import {
+ Form,
+ FormGroup,
+ Checkbox,
+ InputGroup,
+ Button,
+ TextInput,
+} from '@patternfly/react-core';
import { required } from '@util/validators';
import PropTypes from 'prop-types';
+import { SyncAltIcon } from '@patternfly/react-icons';
+import { useParams } from 'react-router-dom';
+import AnsibleSelect from '@components/AnsibleSelect';
+import { WorkflowJobTemplatesAPI, CredentialTypesAPI } from '@api';
import FormRow from '@components/FormRow';
-import FormField, { FieldTooltip } from '@components/FormField';
+import FormField, { FieldTooltip, CheckboxField } from '@components/FormField';
import OrganizationLookup from '@components/Lookup/OrganizationLookup';
+import CredentialLookup from '@components/Lookup/CredentialLookup';
import { InventoryLookup } from '@components/Lookup';
import { VariablesField } from '@components/CodeMirrorInput';
import FormActionGroup from '@components/FormActionGroup';
import ContentError from '@components/ContentError';
+import styled from 'styled-components';
import LabelSelect from './LabelSelect';
+const GridFormGroup = styled(FormGroup)`
+ & > label {
+ grid-column: 1 / -1;
+ }
+
+ && {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ }
+`;
+
function WorkflowJobTemplateForm({
handleSubmit,
handleCancel,
i18n,
template = {},
+ className,
+ webhook_key,
}) {
+ const urlOrigin = window.location.origin;
+ const { id } = useParams();
const [contentError, setContentError] = useState(null);
const [inventory, setInventory] = useState(
template?.summary_fields?.inventory || null
@@ -29,10 +56,77 @@ function WorkflowJobTemplateForm({
const [organization, setOrganization] = useState(
template?.summary_fields?.organization || null
);
+ const [webHookKey, setWebHookKey] = useState(webhook_key);
+ const [credTypeId, setCredentialTypeId] = useState();
+ const [webhookService, setWebHookService] = useState(
+ template.webhook_service || ''
+ );
+ const [hasWebhooks, setHasWebhooks] = useState(
+ webhookService !== '' || false
+ );
+ const [webhookCredential, setWebHookCredential] = useState(
+ template?.summary_fields?.webhook_credential || null
+ );
+
+ const webhookServiceOptions = [
+ {
+ value: '',
+ key: '',
+ label: i18n._(t`Choose a Webhook Service`),
+ isDisabled: true,
+ },
+ {
+ value: 'github',
+ key: 'github',
+ namespace: 'git_hub',
+ label: i18n._(t`GitHub`),
+ isDisabled: false,
+ },
+ {
+ value: 'gitlab',
+ key: 'gitlab',
+ namespace: 'git_lab',
+ label: i18n._(t`Git Lab`),
+ isDisabled: false,
+ },
+ ];
+
+ useEffect(() => {
+ if (!webhookService) {
+ return;
+ }
+ const loadCredentialType = async () => {
+ try {
+ const {
+ data: { results },
+ } = await CredentialTypesAPI.read({
+ namespace: webhookService.includes('hub')
+ ? 'github_token'
+ : 'gitlab_token',
+ });
+ setCredentialTypeId(results[0].id);
+ } catch (err) {
+ setContentError(err);
+ }
+ };
+ loadCredentialType();
+ }, [webhookService]);
+
+ const changeWebhookKey = async () => {
+ try {
+ const {
+ data: { webhook_key: key },
+ } = await WorkflowJobTemplatesAPI.updateWebhookKey(id);
+ setWebHookKey(key);
+ } catch (err) {
+ setContentError(err);
+ }
+ };
if (contentError) {
return ;
}
+
return (
(
form.setFieldTouched('inventory')}
- tooltip={i18n._(t`Select the inventory containing the hosts
- you want this job to manage.`)}
+ tooltip={i18n._(
+ t`Select an inventory for the workflow. This inventory is applied to all job template nodes that prompt for an inventory.`
+ )}
isValid={!form.touched.inventory || !form.errors.inventory}
helperTextInvalid={form.errors.inventory}
onChange={value => {
@@ -109,7 +209,6 @@ function WorkflowJobTemplateForm({
);
setInventory(value);
}}
- touched={form.touched.inventory}
error={form.errors.inventory}
/>
)}
@@ -126,18 +225,34 @@ function WorkflowJobTemplateForm({
/>
-
+ >
+
+
+
- {({ field, form }) => (
-
+ {({ form, field }) => (
+
+
+
+ {i18n._(t`Webhooks`)}
+
+
+ }
+ isChecked={hasWebhooks}
+ onChange={checked => {
+ setHasWebhooks(checked);
+ }}
+ />
+
+
+ {hasWebhooks && (
+ <>
+
+ {template.related && (
+
+
+
+
+ )}
+ {template.related && (
+
+
+
+
+
+
+
+ )}
+
+ {({ form }) => {
+ const isValid =
+ !form.errors.webhook_service;
+ return (
+
+
+ {
+ setWebHookService(val);
+ setWebHookCredential(null);
+ form.setFieldValue('webhook_credential', null);
+ }}
+ />
+
+ );
+ }}
+
+
+ {credTypeId && (
+
+
+ {({ form }) => {
+ return (
+
+ {
+ setWebHookCredential(value);
+ form.setFieldValue(
+ 'webhook_credential',
+ value.id
+ );
+ }}
+ value={webhookCredential}
+ />
+
+ );
+ }}
+
+
+ )}
+ >
+ )}