1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 16:51:11 +03:00

Fixed a number of workflow visualizer bugs. Added loading spinners while data is being loaded/processed.

This commit is contained in:
mabashian 2018-11-14 16:48:32 -05:00
parent 266831e26d
commit 4a6a3b27fa
8 changed files with 397 additions and 268 deletions

View File

@ -128,7 +128,9 @@ function TemplatesStrings (BaseString) {
EDIT_LINK: t.s('EDIT LINK'),
VIEW_LINK: t.s('VIEW LINK'),
NEW_LINK: t.s('Please click on an available node to form a new link.'),
UNLINK: t.s('UNLINK')
UNLINK: t.s('UNLINK'),
READ_ONLY_PROMPT_VALUES: t.s('The following promptable values were provided when this node was created:'),
READ_ONLY_NO_PROMPT_VALUES: t.s('No promptable values were provided when this node was created.')
};
}

View File

@ -114,7 +114,11 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
// There's something off with the math on the root node...
if (d.source.id === 1) {
sourceY = sourceY + 10;
if (scope.mode === "details") {
sourceY = sourceY + 17;
} else {
sourceY = sourceY + 10;
}
}
let points = [{
@ -1279,7 +1283,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function node_click() {
this.on("click", function(d) {
if(d.id !== scope.graphState.nodeBeingAdded && !scope.readOnly){
if(d.id !== scope.graphState.nodeBeingAdded){
if(scope.graphState.isLinkMode && !d.isInvalidLinkTarget && scope.graphState.addLinkSource !== d.id) {
$('.WorkflowChart-potentialLink').remove();
scope.selectNodeForLinking({

View File

@ -1,42 +1,5 @@
export default [function(){
return {
generateDepthMap: (arrayOfLinks) => {
let depthMap = {};
let nodesWithChildren = {};
let walkBranch = (nodeId, depth) => {
depthMap[nodeId] = depthMap[nodeId] ? (depth > depthMap[nodeId] ? depth : depthMap[nodeId]) : depth;
if (nodesWithChildren[nodeId]) {
_.forEach(nodesWithChildren[nodeId].children, (childNodeId) => {
walkBranch(childNodeId, depth+1);
});
}
};
let rootNodeIds = [];
arrayOfLinks.forEach(link => {
// link.source.index of 0 is our artificial start node
if (link.source.index !== 0) {
if (!nodesWithChildren[link.source.id]) {
nodesWithChildren[link.source.id] = {
children: []
};
}
nodesWithChildren[link.source.id].children.push(link.target.id);
} else {
// Store the fact that might be a root node
rootNodeIds.push(link.target.id);
}
});
_.forEach(rootNodeIds, function(rootNodeId) {
walkBranch(rootNodeId, 1);
depthMap[rootNodeId] = 1;
});
return depthMap;
},
generateArraysOfNodesAndLinks: function(allNodes) {
let nonRootNodeIds = [];
let allNodeIds = [];
@ -77,7 +40,7 @@ export default [function(){
nodeObj.job = node.summary_fields.job;
}
if(node.summary_fields.unified_job_template) {
nodeObj.unifiedJobTemplate = node.summary_fields.unified_job_template;
nodeRef[nodeIdCounter].unifiedJobTemplate = nodeObj.unifiedJobTemplate = node.summary_fields.unified_job_template;
}
arrayOfNodesForChart.push(nodeObj);

View File

@ -7,11 +7,11 @@
export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService', 'Rest', '$q',
'TemplatesStrings', 'CreateSelect2', 'Empty', 'generateList', 'QuerySet',
'GetBasePath', 'TemplateList', 'ProjectList', 'InventorySourcesList', 'ProcessErrors',
'i18n',
'i18n', 'ParseTypeChange',
function($scope, TemplatesService, JobTemplate, PromptService, Rest, $q,
TemplatesStrings, CreateSelect2, Empty, generateList, qs,
GetBasePath, TemplateList, ProjectList, InventorySourcesList, ProcessErrors,
i18n
i18n, ParseTypeChange
) {
let promptWatcher, credentialsWatcher, surveyQuestionWatcher, listPromises = [];
@ -139,229 +139,254 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
const finishConfiguringEdit = () => {
let jobTemplate = new JobTemplate();
if (!$scope.readOnly) {
let jobTemplate = new JobTemplate();
if (_.get($scope, 'nodeConfig.node.promptData') && !_.isEmpty($scope.nodeConfig.node.promptData)) {
$scope.promptData = _.cloneDeep($scope.nodeConfig.node.promptData);
const launchConf = $scope.promptData.launchConf;
if (_.get($scope, 'nodeConfig.node.promptData') && !_.isEmpty($scope.nodeConfig.node.promptData)) {
$scope.promptData = _.cloneDeep($scope.nodeConfig.node.promptData);
const launchConf = $scope.promptData.launchConf;
if (!launchConf.survey_enabled &&
!launchConf.ask_inventory_on_launch &&
!launchConf.ask_credential_on_launch &&
!launchConf.ask_verbosity_on_launch &&
!launchConf.ask_job_type_on_launch &&
!launchConf.ask_limit_on_launch &&
!launchConf.ask_tags_on_launch &&
!launchConf.ask_skip_tags_on_launch &&
!launchConf.ask_diff_mode_on_launch &&
!launchConf.credential_needed_to_start &&
!launchConf.ask_variables_on_launch &&
launchConf.variables_needed_to_start.length === 0) {
$scope.showPromptButton = false;
$scope.promptModalMissingReqFields = false;
} else {
$scope.showPromptButton = true;
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeConfig.node.originalNodeObject.summary_fields.inventory')) {
$scope.promptModalMissingReqFields = true;
if (!launchConf.survey_enabled &&
!launchConf.ask_inventory_on_launch &&
!launchConf.ask_credential_on_launch &&
!launchConf.ask_verbosity_on_launch &&
!launchConf.ask_job_type_on_launch &&
!launchConf.ask_limit_on_launch &&
!launchConf.ask_tags_on_launch &&
!launchConf.ask_skip_tags_on_launch &&
!launchConf.ask_diff_mode_on_launch &&
!launchConf.credential_needed_to_start &&
!launchConf.ask_variables_on_launch &&
launchConf.variables_needed_to_start.length === 0) {
$scope.showPromptButton = false;
$scope.promptModalMissingReqFields = false;
} else {
$scope.promptModalMissingReqFields = false;
}
}
$scope.nodeFormDataLoaded = true;
} else if (
_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.unified_job_type') === 'job_template' ||
_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.type') === 'job_template'
) {
let promises = [jobTemplate.optionsLaunch($scope.nodeConfig.node.fullUnifiedJobTemplateObject.id), jobTemplate.getLaunch($scope.nodeConfig.node.fullUnifiedJobTemplateObject.id)];
$scope.showPromptButton = true;
if (_.has($scope, 'nodeConfig.node.originalNodeObject.related.credentials')) {
Rest.setUrl($scope.nodeConfig.node.originalNodeObject.related.credentials);
promises.push(Rest.get());
}
$q.all(promises)
.then((responses) => {
let launchOptions = responses[0].data,
launchConf = responses[1].data,
workflowNodeCredentials = responses[2] ? responses[2].data.results : [];
let prompts = PromptService.processPromptValues({
launchConf: responses[1].data,
launchOptions: responses[0].data,
currentValues: $scope.nodeConfig.node.originalNodeObject
});
let defaultCredsWithoutOverrides = [];
prompts.credentials.previousOverrides = _.cloneDeep(workflowNodeCredentials);
const credentialHasScheduleOverride = (templateDefaultCred) => {
let credentialHasOverride = false;
workflowNodeCredentials.forEach((scheduleCred) => {
if (templateDefaultCred.credential_type === scheduleCred.credential_type) {
if (
(!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) ||
(templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id)
) {
credentialHasOverride = true;
}
}
});
return credentialHasOverride;
};
if (_.has(launchConf, 'defaults.credentials')) {
launchConf.defaults.credentials.forEach((defaultCred) => {
if (!credentialHasScheduleOverride(defaultCred)) {
defaultCredsWithoutOverrides.push(defaultCred);
}
});
}
prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
if ((!$scope.nodeConfig.node.fullUnifiedJobTemplateObject.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeConfig.node.fullUnifiedJobTemplateObject.project) {
$scope.selectedTemplateInvalid = true;
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeConfig.node.originalNodeObject.summary_fields.inventory')) {
$scope.promptModalMissingReqFields = true;
} else {
$scope.selectedTemplateInvalid = false;
$scope.promptModalMissingReqFields = false;
}
}
$scope.nodeFormDataLoaded = true;
} else if (
_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.unified_job_type') === 'job_template' ||
_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.type') === 'job_template'
) {
let promises = [jobTemplate.optionsLaunch($scope.nodeConfig.node.fullUnifiedJobTemplateObject.id), jobTemplate.getLaunch($scope.nodeConfig.node.fullUnifiedJobTemplateObject.id)];
let credentialRequiresPassword = false;
if (_.has($scope, 'nodeConfig.node.originalNodeObject.related.credentials')) {
Rest.setUrl($scope.nodeConfig.node.originalNodeObject.related.credentials);
promises.push(Rest.get());
}
prompts.credentials.value.forEach((credential) => {
if(credential.inputs) {
if ((credential.inputs.password && credential.inputs.password === "ASK") ||
(credential.inputs.become_password && credential.inputs.become_password === "ASK") ||
(credential.inputs.ssh_key_unlock && credential.inputs.ssh_key_unlock === "ASK") ||
(credential.inputs.vault_password && credential.inputs.vault_password === "ASK")
) {
$q.all(promises)
.then((responses) => {
let launchOptions = responses[0].data,
launchConf = responses[1].data,
workflowNodeCredentials = responses[2] ? responses[2].data.results : [];
let prompts = PromptService.processPromptValues({
launchConf: responses[1].data,
launchOptions: responses[0].data,
currentValues: $scope.nodeConfig.node.originalNodeObject
});
let defaultCredsWithoutOverrides = [];
prompts.credentials.previousOverrides = _.cloneDeep(workflowNodeCredentials);
const credentialHasScheduleOverride = (templateDefaultCred) => {
let credentialHasOverride = false;
workflowNodeCredentials.forEach((scheduleCred) => {
if (templateDefaultCred.credential_type === scheduleCred.credential_type) {
if (
(!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) ||
(templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id)
) {
credentialHasOverride = true;
}
}
});
return credentialHasOverride;
};
if (_.has(launchConf, 'defaults.credentials')) {
launchConf.defaults.credentials.forEach((defaultCred) => {
if (!credentialHasScheduleOverride(defaultCred)) {
defaultCredsWithoutOverrides.push(defaultCred);
}
});
}
prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
if ((!$scope.nodeConfig.node.fullUnifiedJobTemplateObject.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeConfig.node.fullUnifiedJobTemplateObject.project) {
$scope.selectedTemplateInvalid = true;
} else {
$scope.selectedTemplateInvalid = false;
}
let credentialRequiresPassword = false;
prompts.credentials.value.forEach((credential) => {
if(credential.inputs) {
if ((credential.inputs.password && credential.inputs.password === "ASK") ||
(credential.inputs.become_password && credential.inputs.become_password === "ASK") ||
(credential.inputs.ssh_key_unlock && credential.inputs.ssh_key_unlock === "ASK") ||
(credential.inputs.vault_password && credential.inputs.vault_password === "ASK")
) {
credentialRequiresPassword = true;
}
} else if (credential.passwords_needed && credential.passwords_needed.length > 0) {
credentialRequiresPassword = true;
}
} else if (credential.passwords_needed && credential.passwords_needed.length > 0) {
credentialRequiresPassword = true;
}
});
});
$scope.credentialRequiresPassword = credentialRequiresPassword;
$scope.credentialRequiresPassword = credentialRequiresPassword;
if (!launchConf.survey_enabled &&
!launchConf.ask_inventory_on_launch &&
!launchConf.ask_credential_on_launch &&
!launchConf.ask_verbosity_on_launch &&
!launchConf.ask_job_type_on_launch &&
!launchConf.ask_limit_on_launch &&
!launchConf.ask_tags_on_launch &&
!launchConf.ask_skip_tags_on_launch &&
!launchConf.ask_diff_mode_on_launch &&
!launchConf.credential_needed_to_start &&
!launchConf.ask_variables_on_launch &&
launchConf.variables_needed_to_start.length === 0) {
$scope.showPromptButton = false;
$scope.promptModalMissingReqFields = false;
$scope.nodeFormDataLoaded = true;
} else {
$scope.showPromptButton = true;
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeConfig.node.originalNodeObject.summary_fields.inventory')) {
$scope.promptModalMissingReqFields = true;
if (!launchConf.survey_enabled &&
!launchConf.ask_inventory_on_launch &&
!launchConf.ask_credential_on_launch &&
!launchConf.ask_verbosity_on_launch &&
!launchConf.ask_job_type_on_launch &&
!launchConf.ask_limit_on_launch &&
!launchConf.ask_tags_on_launch &&
!launchConf.ask_skip_tags_on_launch &&
!launchConf.ask_diff_mode_on_launch &&
!launchConf.credential_needed_to_start &&
!launchConf.ask_variables_on_launch &&
launchConf.variables_needed_to_start.length === 0) {
$scope.showPromptButton = false;
$scope.promptModalMissingReqFields = false;
$scope.nodeFormDataLoaded = true;
} else {
$scope.promptModalMissingReqFields = false;
}
$scope.showPromptButton = true;
if (responses[1].data.survey_enabled) {
// go out and get the survey questions
jobTemplate.getSurveyQuestions($scope.nodeConfig.node.fullUnifiedJobTemplateObject.id)
.then((surveyQuestionRes) => {
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeConfig.node.originalNodeObject.summary_fields.inventory')) {
$scope.promptModalMissingReqFields = true;
} else {
$scope.promptModalMissingReqFields = false;
}
let processed = PromptService.processSurveyQuestions({
surveyQuestions: surveyQuestionRes.data.spec,
extra_data: _.cloneDeep($scope.nodeConfig.node.originalNodeObject.extra_data)
});
if (responses[1].data.survey_enabled) {
// go out and get the survey questions
jobTemplate.getSurveyQuestions($scope.nodeConfig.node.fullUnifiedJobTemplateObject.id)
.then((surveyQuestionRes) => {
$scope.missingSurveyValue = processed.missingSurveyValue;
$scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data);
$scope.nodeConfig.node.promptData = $scope.promptData = {
launchConf: launchConf,
launchOptions: launchOptions,
prompts: prompts,
surveyQuestions: surveyQuestionRes.data.spec,
template: $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id
};
surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
let missingSurveyValue = false;
_.each($scope.promptData.surveyQuestions, (question) => {
if (question.required && (Empty(question.model) || question.model === [])) {
missingSurveyValue = true;
}
let processed = PromptService.processSurveyQuestions({
surveyQuestions: surveyQuestionRes.data.spec,
extra_data: _.cloneDeep($scope.nodeConfig.node.originalNodeObject.extra_data)
});
$scope.missingSurveyValue = missingSurveyValue;
}, true);
checkCredentialsForRequiredPasswords();
$scope.missingSurveyValue = processed.missingSurveyValue;
watchForPromptChanges();
$scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data);
$scope.nodeFormDataLoaded = true;
});
} else {
$scope.nodeConfig.node.promptData = $scope.promptData = {
launchConf: launchConf,
launchOptions: launchOptions,
prompts: prompts,
template: $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id
};
$scope.nodeConfig.node.promptData = $scope.promptData = {
launchConf: launchConf,
launchOptions: launchOptions,
prompts: prompts,
surveyQuestions: surveyQuestionRes.data.spec,
template: $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id
};
checkCredentialsForRequiredPasswords();
surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
let missingSurveyValue = false;
_.each($scope.promptData.surveyQuestions, (question) => {
if (question.required && (Empty(question.model) || question.model === [])) {
missingSurveyValue = true;
}
});
$scope.missingSurveyValue = missingSurveyValue;
}, true);
watchForPromptChanges();
checkCredentialsForRequiredPasswords();
$scope.nodeFormDataLoaded = true;
watchForPromptChanges();
$scope.nodeFormDataLoaded = true;
});
} else {
$scope.nodeConfig.node.promptData = $scope.promptData = {
launchConf: launchConf,
launchOptions: launchOptions,
prompts: prompts,
template: $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id
};
checkCredentialsForRequiredPasswords();
watchForPromptChanges();
$scope.nodeFormDataLoaded = true;
}
}
}
});
} else {
$scope.nodeFormDataLoaded = true;
}
});
} else {
$scope.nodeFormDataLoaded = true;
}
if (_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject')) {
if (_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.type') === "job_template") {
if (_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject')) {
if (_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.type') === "job_template") {
$scope.activeTab = "jobs";
}
$scope.selectedTemplate = $scope.nodeConfig.node.fullUnifiedJobTemplateObject;
if ($scope.selectedTemplate.unified_job_type) {
switch ($scope.selectedTemplate.unified_job_type) {
case "job":
$scope.activeTab = "jobs";
break;
case "project_update":
$scope.activeTab = "project_syncs";
break;
case "inventory_update":
$scope.activeTab = "inventory_syncs";
break;
}
} else if ($scope.selectedTemplate.type) {
switch ($scope.selectedTemplate.type) {
case "job_template":
$scope.activeTab = "jobs";
break;
case "project":
$scope.activeTab = "project_syncs";
break;
case "inventory_source":
$scope.activeTab = "inventory_syncs";
break;
}
}
} else {
$scope.activeTab = "jobs";
}
$scope.selectedTemplate = $scope.nodeConfig.node.fullUnifiedJobTemplateObject;
if ($scope.selectedTemplate.unified_job_type) {
switch ($scope.selectedTemplate.unified_job_type) {
case "job":
$scope.activeTab = "jobs";
break;
case "project_update":
$scope.activeTab = "project_syncs";
break;
case "inventory_update":
$scope.activeTab = "inventory_syncs";
break;
}
} else if ($scope.selectedTemplate.type) {
switch ($scope.selectedTemplate.type) {
case "job_template":
$scope.activeTab = "jobs";
break;
case "project":
$scope.activeTab = "project_syncs";
break;
case "inventory_source":
$scope.activeTab = "inventory_syncs";
break;
}
}
} else {
$scope.activeTab = "jobs";
$scope.jobTags = $scope.nodeConfig.node.originalNodeObject.job_tags ? $scope.nodeConfig.node.originalNodeObject.job_tags.split(',').map((tag) => (tag)) : [];
$scope.skipTags = $scope.nodeConfig.node.originalNodeObject.skip_tags ? $scope.nodeConfig.node.originalNodeObject.skip_tags.split(',').map((tag) => (tag)) : [];
$scope.showJobTags = true;
$scope.showSkipTags = true;
if (!$.isEmptyObject($scope.nodeConfig.node.originalNodeObject.extra_data)) {
$scope.extraVars = '---\n' + jsyaml.safeDump($scope.nodeConfig.node.originalNodeObject.extra_data);
$scope.showExtraVars = true;
$scope.parseType = 'yaml';
ParseTypeChange({
scope: $scope,
variable: 'extraVars',
field_id: 'workflow_node_form_extra_vars',
readOnly: true
});
} else {
$scope.extraVars = null;
$scope.showExtraVars = false;
}
$scope.nodeFormDataLoaded = true;
}
};

View File

@ -1,11 +1,11 @@
<div ng-show="nodeFormDataLoaded">
<div class="WorkflowMaker-formTitle ng-binding">{{nodeConfig.mode === 'edit' ? node.unifiedJobTemplate.name : strings.get('workflow_maker.ADD_A_TEMPLATE')}}</div>
<div class="Form-tabHolder">
<div class="WorkflowMaker-formTitle">{{nodeConfig.mode === 'edit' ? nodeConfig.node.unifiedJobTemplate.name : strings.get('workflow_maker.ADD_A_TEMPLATE')}}</div>
<div class="Form-tabHolder" ng-if="!readOnly">
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': activeTab === 'jobs'}" ng-click="activeTab = 'jobs'">{{strings.get('workflow_maker.JOBS')}}</div>
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': activeTab === 'project_syncs'}" ng-click="activeTab = 'project_syncs'">{{strings.get('workflow_maker.PROJECT_SYNC')}}</div>
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': activeTab === 'inventory_syncs'}" ng-click="activeTab = 'inventory_syncs'">{{strings.get('workflow_maker.INVENTORY_SYNC')}}</div>
</div>
<div class="WorkflowMaker-formLists">
<div class="WorkflowMaker-formLists" ng-if="!readOnly">
<div id="workflow-jobs-list" ng-show="activeTab === 'jobs'">
<div ng-hide="wf_maker_templates.length === 0 && (searchTags | isEmpty)">
<smart-search django-model="unified_job_templates" base-path="unified_job_templates" iterator="wf_maker_template" dataset="wf_maker_template_dataset" list="templateList" collection="wf_maker_templates" default-params="wf_maker_template_default_params" query-set="wf_maker_template_queryset" search-bar-full-width="true" search-tags="searchTags">
@ -141,6 +141,98 @@
</select>
</div>
</div>
<div ng-if="readOnly">
<div
class="WorkflowMaker-readOnlyPromptText"
ng-show="nodeConfig.node.originalNodeObject.job_type !== null ||
(promptData.prompts.credentials.value && promptData.prompts.credentials.value.length > 0) ||
nodeConfig.node.originalNodeObject.inventory !== null ||
nodeConfig.node.originalNodeObject.limit !== null ||
nodeConfig.node.originalNodeObject.verbosity !== null ||
nodeConfig.node.originalNodeObject.job_tags !== null ||
nodeConfig.node.originalNodeObject.skip_tags !== null ||
nodeConfig.node.originalNodeObject.diff_mode !== null ||
showExtraVars">
{{:: strings.get('workflow_maker.READ_ONLY_PROMPT_VALUES')}}
</div>
<div
class="WorkflowMaker-readOnlyPromptText"
ng-show="!(nodeConfig.node.originalNodeObject.job_type !== null ||
(promptData.prompts.credentials.value && promptData.prompts.credentials.value.length > 0) ||
nodeConfig.node.originalNodeObject.inventory !== null ||
nodeConfig.node.originalNodeObject.limit !== null ||
nodeConfig.node.originalNodeObject.verbosity !== null ||
nodeConfig.node.originalNodeObject.job_tags !== null ||
nodeConfig.node.originalNodeObject.skip_tags !== null ||
nodeConfig.node.originalNodeObject.diff_mode !== null ||
showExtraVars)">
{{:: strings.get('workflow_maker.READ_ONLY_NO_PROMPT_VALUES')}}
</div>
<div class="Prompt-previewRow--flex" ng-if="nodeConfig.node.originalNodeObject.job_type !== null">
<div class="Prompt-previewRowTitle">{{:: strings.get('prompt.JOB_TYPE') }}</div>
<div class="Prompt-previewRowValue">
<span ng-if="nodeConfig.node.originalNodeObject.job_type === 'run'">{{:: strings.get('prompt.PLAYBOOK_RUN') }}</span>
<span ng-if="nodeConfig.node.originalNodeObject.job_type === 'check'">{{:: strings.get('prompt.CHECK') }}</span>
</div>
</div>
<div class="Prompt-previewRow--flex" ng-if="nodeConfig.node.originalNodeObject.inventory !== null">
<div class="Prompt-previewRowTitle">{{:: strings.get('prompt.INVENTORY') }}</div>
<div class="Prompt-previewRowValue" ng-bind="nodeConfig.node.originalNodeObject.summary_fields.inventory.name"></div>
</div>
<div class="Prompt-previewRow--flex" ng-if="nodeConfig.node.originalNodeObject.limit !== null">
<div class="Prompt-previewRowTitle">{{:: strings.get('prompt.LIMIT') }}</div>
<div class="Prompt-previewRowValue" ng-bind="nodeConfig.node.originalNodeObject.limit"></div>
</div>
<div class="Prompt-previewRow--flex" ng-if="nodeConfig.node.originalNodeObject.verbosity !== null">
<div class="Prompt-previewRowTitle">{{:: strings.get('prompt.VERBOSITY') }}</div>
<div class="Prompt-previewRowValue" ng-bind="nodeConfig.node.originalNodeObject.verbosity"></div>
</div>
<div class="Prompt-previewRow--noflex" ng-if="nodeConfig.node.originalNodeObject.job_tags !== null">
<div class="Prompt-previewRowTitle">
<span>{{:: strings.get('prompt.JOB_TAGS') }}&nbsp;</span>
<span ng-click="showJobTags = !showJobTags">
<span class="fa fa-caret-down" ng-show="showJobTags" ></span>
<span class="fa fa-caret-left" ng-show="!showJobTags"></span>
</span>
</div>
<div ng-show="showJobTags" class="Prompt-previewTagContainer">
<div class="u-wordwrap" ng-repeat="tag in jobTags">
<div class="LabelList-tag">
<span>{{tag}}</span>
</div>
</div>
</div>
</div>
<div class="Prompt-previewRow--noflex" ng-if="nodeConfig.node.originalNodeObject.skip_tags !== null">
<div class="Prompt-previewRowTitle">
<span>{{:: strings.get('prompt.SKIP_TAGS') }}&nbsp;</span>
<span ng-click="showSkipTags = !showSkipTags">
<span class="fa fa-caret-down" ng-show="showSkipTags" ></span>
<span class="fa fa-caret-left" ng-show="!showSkipTags"></span>
</span>
</div>
<div ng-show="showSkipTags" class="Prompt-previewTagContainer">
<div class="u-wordwrap" ng-repeat="tag in skipTags">
<div class="LabelList-tag">
<span>{{tag}}</span>
</div>
</div>
</div>
</div>
<div class="Prompt-previewRow--flex" ng-if="nodeConfig.node.originalNodeObject.diff_mode !== null">
<div class="Prompt-previewRowTitle">{{:: strings.get('prompt.SHOW_CHANGES') }}</div>
<div class="Prompt-previewRowValue">
<span ng-if="promptData.prompts.diffMode.value">{{:: strings.get('ON') }}</span>
<span ng-if="!promptData.prompts.diffMode.value">{{:: strings.get('OFF') }}</span>
</div>
</div>
<div class="Prompt-previewRow--noflex" ng-show="showExtraVars">
<div class="Prompt-previewRowTitle">{{:: strings.get('prompt.EXTRA_VARIABLES') }}</div>
<div>
<textarea rows="6" ng-model="extraVars" name="node_form_extra_vars" class="form-control Form-textArea Form-textAreaLabel" id="workflow_node_form_extra_vars"></textarea>
</div>
</div>
</div>
<div class="buttons Form-buttons" id="workflow_maker_controls">
<button type="button" class="btn btn-sm Form-primaryButton Form-primaryButton--noMargin" id="workflow_maker_prompt_btn" ng-show="showPromptButton" ng-click="openPromptModal()"> {{:: strings.get('prompt.PROMPT') }}</button>
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_btn" ng-show="!readOnly" ng-click="cancel()"> {{:: strings.get('CANCEL') }}</button>

View File

@ -309,6 +309,10 @@
color: @default-err;
}
.WorkflowMaker-readOnlyPromptText {
margin-bottom: 20px;
}
.Key-list {
margin: 0;
padding: 20px;

View File

@ -7,9 +7,12 @@
export default ['$scope', 'TemplatesService',
'ProcessErrors', 'CreateSelect2', '$q', 'JobTemplateModel',
'Empty', 'PromptService', 'Rest', 'TemplatesStrings', 'WorkflowChartService',
'Wait',
function ($scope, TemplatesService,
ProcessErrors, CreateSelect2, $q, JobTemplate,
Empty, PromptService, Rest, TemplatesStrings, WorkflowChartService) {
Empty, PromptService, Rest, TemplatesStrings, WorkflowChartService,
Wait
) {
$scope.strings = TemplatesStrings;
$scope.preventCredsWithPasswords = true;
@ -90,10 +93,12 @@ export default ['$scope', 'TemplatesService',
$scope.saveWorkflowMaker = function () {
Wait('start');
if ($scope.graphState.arrayOfNodesForChart.length > 1) {
let addPromises = [];
let editPromises = [];
let credentialsToPost = [];
let credentialRequests = [];
Object.keys(nodeRef).map((workflowMakerNodeId) => {
if (nodeRef[workflowMakerNodeId].isNew) {
@ -114,8 +119,8 @@ export default ['$scope', 'TemplatesService',
});
credentialIdsToPost.forEach((credentialToPost) => {
credentialsToPost.push({
id: data.id,
credentialRequests.push({
id: data.data.id,
data: {
id: credentialToPost.id
}
@ -128,6 +133,51 @@ export default ['$scope', 'TemplatesService',
id: nodeRef[workflowMakerNodeId].originalNodeObject.id,
data: buildSendableNodeData(nodeRef[workflowMakerNodeId])
}));
if (_.get(nodeRef[workflowMakerNodeId], 'promptData.launchConf.ask_credential_on_launch')) {
let credentialsNotInPriorCredentials = nodeRef[workflowMakerNodeId].promptData.prompts.credentials.value.filter(function (credFromPrompt) {
let defaultCreds = _.get(nodeRef[workflowMakerNodeId], 'promptData.launchConf.defaults.credentials', []);
return !defaultCreds.some(function (defaultCred) {
return credFromPrompt.id === defaultCred.id;
});
});
let credentialsToAdd = credentialsNotInPriorCredentials.filter(function (credNotInPrior) {
let previousOverrides = _.get(nodeRef[workflowMakerNodeId], 'promptData.prompts.credentials.previousOverrides', []);
return !previousOverrides.some(function (priorCred) {
return credNotInPrior.id === priorCred.id;
});
});
let credentialsToRemove = [];
if (_.has(nodeRef[workflowMakerNodeId], 'promptData.prompts.credentials.previousOverrides')) {
credentialsToRemove = nodeRef[workflowMakerNodeId].promptData.prompts.credentials.previousOverrides.filter(function (priorCred) {
return !credentialsNotInPriorCredentials.some(function (credNotInPrior) {
return priorCred.id === credNotInPrior.id;
});
});
}
credentialsToAdd.forEach((credentialToAdd) => {
credentialRequests.push({
id: nodeRef[workflowMakerNodeId].originalNodeObject.id,
data: {
id: credentialToAdd.id
}
});
});
credentialsToRemove.forEach((credentialToRemove) => {
credentialRequests.push({
id: nodeRef[workflowMakerNodeId].originalNodeObject.id,
data: {
id: credentialToRemove.id,
disassociate: true
}
});
});
}
}
});
@ -222,7 +272,7 @@ export default ['$scope', 'TemplatesService',
case "success":
if (
!nodeRef[sourceChartNodeId].originalNodeObject.success_nodes ||
!nodeRef[sourceChartNodeId].originalNodeObject.success_nodes.includes(nodeRef[targetChartNodeId].id)
!nodeRef[sourceChartNodeId].originalNodeObject.success_nodes.includes(nodeRef[targetChartNodeId].originalNodeObject.id)
) {
associatePromises.push(
TemplatesService.associateWorkflowNode({
@ -236,7 +286,7 @@ export default ['$scope', 'TemplatesService',
case "failure":
if (
!nodeRef[sourceChartNodeId].originalNodeObject.failure_nodes ||
!nodeRef[sourceChartNodeId].originalNodeObject.failure_nodes.includes(nodeRef[targetChartNodeId].id)
!nodeRef[sourceChartNodeId].originalNodeObject.failure_nodes.includes(nodeRef[targetChartNodeId].originalNodeObject.id)
) {
associatePromises.push(
TemplatesService.associateWorkflowNode({
@ -250,7 +300,7 @@ export default ['$scope', 'TemplatesService',
case "always":
if (
!nodeRef[sourceChartNodeId].originalNodeObject.always_nodes ||
!nodeRef[sourceChartNodeId].originalNodeObject.always_nodes.includes(nodeRef[targetChartNodeId].id)
!nodeRef[sourceChartNodeId].originalNodeObject.always_nodes.includes(nodeRef[targetChartNodeId].originalNodeObject.id)
) {
associatePromises.push(
TemplatesService.associateWorkflowNode({
@ -267,7 +317,7 @@ export default ['$scope', 'TemplatesService',
$q.all(disassociatePromises)
.then(function () {
let credentialPromises = credentialsToPost.map(function (request) {
let credentialPromises = credentialRequests.map(function (request) {
return TemplatesService.postWorkflowNodeCredential({
id: request.id,
data: request.data
@ -276,12 +326,14 @@ export default ['$scope', 'TemplatesService',
return $q.all(associatePromises.concat(credentialPromises))
.then(function () {
Wait('stop');
$scope.closeDialog();
});
}).catch(({
data,
status
}) => {
Wait('stop');
ProcessErrors($scope, data, status, null, {});
});
});
@ -294,6 +346,7 @@ export default ['$scope', 'TemplatesService',
$q.all(deletePromises)
.then(function () {
Wait('stop');
$scope.closeDialog();
$state.transitionTo('templates');
});
@ -335,8 +388,6 @@ export default ['$scope', 'TemplatesService',
workflowMakerNodeIdCounter++;
$scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.$broadcast("refreshWorkflowChart");
$scope.formState.showNodeForm = true;
@ -383,8 +434,6 @@ export default ['$scope', 'TemplatesService',
workflowMakerNodeIdCounter++;
$scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.$broadcast("refreshWorkflowChart");
$scope.formState.showNodeForm = true;
@ -480,7 +529,6 @@ export default ['$scope', 'TemplatesService',
}
}
$scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
} else if ($scope.nodeConfig.mode === "edit") {
$scope.graphState.nodeBeingEdited = null;
}
@ -560,7 +608,6 @@ export default ['$scope', 'TemplatesService',
// User is going from editing one link to editing another
if ($scope.linkConfig.mode === "add") {
$scope.graphState.arrayOfLinksForChart.splice($scope.graphState.arrayOfLinksForChart.length-1, 1);
$scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
}
setupLinkEdit();
}
@ -603,8 +650,6 @@ export default ['$scope', 'TemplatesService',
}
});
$scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.graphState.isLinkMode = false;
} else {
// This is the first node selected
@ -689,8 +734,6 @@ export default ['$scope', 'TemplatesService',
}
}
$scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.formState.showLinkForm = false;
$scope.linkConfig = null;
$scope.$broadcast("refreshWorkflowChart");
@ -713,7 +756,6 @@ export default ['$scope', 'TemplatesService',
edgeType: "always"
});
}
$scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
}
$scope.graphState.linkBeingEdited = null;
$scope.graphState.addLinkSource = null;
@ -804,8 +846,6 @@ export default ['$scope', 'TemplatesService',
$scope.deleteOverlayVisible = false;
$scope.graphState.depthMap = WorkflowChartService.generateDepthMap($scope.graphState.arrayOfLinksForChart);
$scope.nodeToBeDeleted = null;
$scope.deleteOverlayVisible = false;
@ -848,7 +888,7 @@ export default ['$scope', 'TemplatesService',
let page = 1;
let getNodes = function () {
// Get the workflow nodes
Wait('start');
TemplatesService.getWorkflowJobTemplateNodes($scope.workflowJobTemplateObj.id, page)
.then(function (data) {
for (var i = 0; i < data.data.results.length; i++) {
@ -864,11 +904,12 @@ export default ['$scope', 'TemplatesService',
({arrayOfNodesForChart, arrayOfLinksForChart, chartNodeIdToIndexMapping, nodeIdToChartNodeIdMapping, nodeRef, workflowMakerNodeIdCounter} = WorkflowChartService.generateArraysOfNodesAndLinks(allNodes));
let depthMap = WorkflowChartService.generateDepthMap(arrayOfLinksForChart);
$scope.graphState = { arrayOfNodesForChart, arrayOfLinksForChart };
$scope.graphState = { arrayOfNodesForChart, arrayOfLinksForChart, depthMap };
Wait('stop');
}
}, function ({ data, status, config }) {
Wait('stop');
ProcessErrors($scope, data, status, null, {
hdr: $scope.strings.get('error.HEADER'),
msg: $scope.strings.get('error.CALL', {

View File

@ -175,9 +175,7 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
({arrayOfNodesForChart, arrayOfLinksForChart, chartNodeIdToIndexMapping, nodeIdToChartNodeIdMapping} = WorkflowChartService.generateArraysOfNodesAndLinks(workflowNodes));
let depthMap = WorkflowChartService.generateDepthMap(arrayOfLinksForChart);
$scope.graphState = { arrayOfNodesForChart, arrayOfLinksForChart, depthMap };
$scope.graphState = { arrayOfNodesForChart, arrayOfLinksForChart };
}
$scope.toggleStdoutFullscreen = function() {