{i18n._(t`Node Type`)}
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/useNodeTypeStep.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/useNodeTypeStep.jsx
index 787c30674d..d7c83097e9 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/useNodeTypeStep.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/useNodeTypeStep.jsx
@@ -6,31 +6,62 @@ import StepName from '../../../../../../components/LaunchPrompt/steps/StepName';
const STEP_ID = 'nodeType';
-export default function useNodeTypeStep(i18n) {
+export default function useNodeTypeStep(launchConfig, i18n) {
const [, meta] = useField('nodeType');
const [approvalNameField] = useField('approvalName');
const [nodeTypeField, ,] = useField('nodeType');
- const [nodeResourceField] = useField('nodeResource');
+ const [nodeResourceField, nodeResourceMeta] = useField({
+ name: 'nodeResource',
+ validate: value => {
+ if (
+ value?.type === 'job_template' &&
+ (!value?.project ||
+ value?.project === null ||
+ ((!value?.inventory || value?.inventory === null) &&
+ !value?.ask_inventory_on_launch))
+ ) {
+ return i18n._(
+ t`Job Templates with a missing inventory or project cannot be selected when creating or editing nodes. Select another template or fix the missing fields to proceed.`
+ );
+ }
+ return undefined;
+ },
+ });
+
+ const formError = !!meta.error || !!nodeResourceMeta.error;
return {
- step: getStep(i18n, nodeTypeField, approvalNameField, nodeResourceField),
+ step: getStep(
+ i18n,
+ nodeTypeField,
+ approvalNameField,
+ nodeResourceField,
+ formError
+ ),
initialValues: getInitialValues(),
isReady: true,
contentError: null,
- hasError: !!meta.error,
+ hasError: formError,
setTouched: setFieldTouched => {
setFieldTouched('nodeType', true, false);
},
validate: () => {},
};
}
-function getStep(i18n, nodeTypeField, approvalNameField, nodeResourceField) {
+function getStep(
+ i18n,
+ nodeTypeField,
+ approvalNameField,
+ nodeResourceField,
+ formError
+) {
const isEnabled = () => {
if (
(nodeTypeField.value !== 'workflow_approval_template' &&
nodeResourceField.value === null) ||
(nodeTypeField.value === 'workflow_approval_template' &&
- approvalNameField.value === undefined)
+ approvalNameField.value === undefined) ||
+ formError
) {
return false;
}
@@ -39,7 +70,7 @@ function getStep(i18n, nodeTypeField, approvalNameField, nodeResourceField) {
return {
id: STEP_ID,
name: (
-
+
{i18n._(t`Node type`)}
),
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/useWorkflowNodeSteps.js b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/useWorkflowNodeSteps.js
index b6a80d72f2..e882a93329 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/useWorkflowNodeSteps.js
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/useWorkflowNodeSteps.js
@@ -1,5 +1,6 @@
import { useContext, useState, useEffect } from 'react';
import { useFormikContext } from 'formik';
+import { t } from '@lingui/macro';
import useInventoryStep from '../../../../../components/LaunchPrompt/steps/useInventoryStep';
import useCredentialsStep from '../../../../../components/LaunchPrompt/steps/useCredentialsStep';
import useOtherPromptsStep from '../../../../../components/LaunchPrompt/steps/useOtherPromptsStep';
@@ -29,7 +30,12 @@ function showPreviewStep(nodeType, launchConfig) {
);
}
-const getNodeToEditDefaultValues = (launchConfig, surveyConfig, nodeToEdit) => {
+const getNodeToEditDefaultValues = (
+ launchConfig,
+ surveyConfig,
+ nodeToEdit,
+ resourceDefaultCredentials
+) => {
const initialValues = {
nodeResource: nodeToEdit?.fullUnifiedJobTemplate || null,
nodeType: nodeToEdit?.fullUnifiedJobTemplate?.type || 'job_template',
@@ -70,35 +76,34 @@ const getNodeToEditDefaultValues = (launchConfig, surveyConfig, nodeToEdit) => {
} else if (nodeToEdit?.originalNodeCredentials) {
const defaultCredsWithoutOverrides = [];
- const credentialHasScheduleOverride = templateDefaultCred => {
- let credentialHasOverride = false;
- nodeToEdit.originalNodeCredentials.forEach(scheduleCred => {
+ const credentialHasOverride = templateDefaultCred => {
+ let hasOverride = false;
+ nodeToEdit.originalNodeCredentials.forEach(nodeCredential => {
if (
- templateDefaultCred.credential_type === scheduleCred.credential_type
+ templateDefaultCred.credential_type ===
+ nodeCredential.credential_type
) {
if (
(!templateDefaultCred.vault_id &&
- !scheduleCred.inputs.vault_id) ||
+ !nodeCredential.inputs.vault_id) ||
(templateDefaultCred.vault_id &&
- scheduleCred.inputs.vault_id &&
- templateDefaultCred.vault_id === scheduleCred.inputs.vault_id)
+ nodeCredential.inputs.vault_id &&
+ templateDefaultCred.vault_id === nodeCredential.inputs.vault_id)
) {
- credentialHasOverride = true;
+ hasOverride = true;
}
}
});
- return credentialHasOverride;
+ return hasOverride;
};
- if (nodeToEdit?.fullUnifiedJobTemplate?.summary_fields?.credentials) {
- nodeToEdit.fullUnifiedJobTemplate.summary_fields.credentials.forEach(
- defaultCred => {
- if (!credentialHasScheduleOverride(defaultCred)) {
- defaultCredsWithoutOverrides.push(defaultCred);
- }
+ if (resourceDefaultCredentials) {
+ resourceDefaultCredentials.forEach(defaultCred => {
+ if (!credentialHasOverride(defaultCred)) {
+ defaultCredsWithoutOverrides.push(defaultCred);
}
- );
+ });
}
initialValues.credentials = nodeToEdit.originalNodeCredentials.concat(
@@ -179,17 +184,27 @@ export default function useWorkflowNodeSteps(
surveyConfig,
i18n,
resource,
- askLinkType
+ askLinkType,
+ resourceDefaultCredentials
) {
const { nodeToEdit } = useContext(WorkflowStateContext);
- const { resetForm, values: formikValues } = useFormikContext();
+ const {
+ resetForm,
+ values: formikValues,
+ errors: formikErrors,
+ } = useFormikContext();
const [visited, setVisited] = useState({});
const steps = [
useRunTypeStep(i18n, askLinkType),
- useNodeTypeStep(i18n),
+ useNodeTypeStep(launchConfig, i18n),
useInventoryStep(launchConfig, resource, i18n, visited),
- useCredentialsStep(launchConfig, resource, i18n),
+ useCredentialsStep(
+ launchConfig,
+ resource,
+ resourceDefaultCredentials,
+ i18n
+ ),
useOtherPromptsStep(launchConfig, resource, i18n),
useSurveyStep(launchConfig, surveyConfig, resource, i18n, visited),
];
@@ -222,7 +237,8 @@ export default function useWorkflowNodeSteps(
initialValues = getNodeToEditDefaultValues(
launchConfig,
surveyConfig,
- nodeToEdit
+ nodeToEdit,
+ resourceDefaultCredentials
);
} else {
initialValues = steps.reduce((acc, cur) => {
@@ -233,7 +249,23 @@ export default function useWorkflowNodeSteps(
}, {});
}
+ const errors = formikErrors.nodeResource
+ ? {
+ nodeResource: formikErrors.nodeResource,
+ }
+ : {};
+
+ if (
+ !launchConfig?.ask_credential_on_launch &&
+ launchConfig?.passwords_needed_to_start?.length > 0
+ ) {
+ errors.nodeResource = i18n._(
+ t`Job Templates with credentials that prompt for passwords cannot be selected when creating or editing nodes`
+ );
+ }
+
resetForm({
+ errors,
values: {
...initialValues,
nodeResource: formikValues.nodeResource,