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} + /> + + ); + }} + + + )} + + )}