mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Implements workflow convergence without proper layout
This commit is contained in:
parent
07db7a41b3
commit
7b95d2114d
@ -124,9 +124,12 @@ function TemplatesStrings (BaseString) {
|
||||
INVENTORY_WILL_NOT_OVERRIDE: t.s('The inventory of this node will not be overridden by the parent workflow inventory.'),
|
||||
INVENTORY_PROMPT_WILL_OVERRIDE: t.s('The inventory of this node will be overridden if a parent workflow inventory is provided at launch.'),
|
||||
INVENTORY_PROMPT_WILL_NOT_OVERRIDE: t.s('The inventory of this node will not be overridden if a parent workflow inventory is provided at launch.'),
|
||||
EDIT_LINK: ({ parentName, childName }) => t.s('EDIT LINK | {{parentName}} to {{childName}}', { parentName, childName }),
|
||||
VIEW_LINK: ({ parentName, childName }) => t.s('VIEW LINK | {{parentName}} to {{childName}}', { parentName, childName })
|
||||
}
|
||||
ADD_LINK: t.s('ADD LINK'),
|
||||
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')
|
||||
};
|
||||
}
|
||||
|
||||
TemplatesStrings.$inject = ['BaseStringService'];
|
||||
|
||||
@ -14,7 +14,6 @@ import prompt from './prompt/main';
|
||||
import workflowChart from './workflows/workflow-chart/main';
|
||||
import workflowMaker from './workflows/workflow-maker/main';
|
||||
import workflowControls from './workflows/workflow-controls/main';
|
||||
import workflowService from './workflows/workflow.service';
|
||||
import WorkflowForm from './workflows.form';
|
||||
import InventorySourcesList from './inventory-sources.list';
|
||||
import TemplateList from './templates.list';
|
||||
@ -35,7 +34,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
|
||||
workflowChart.name, workflowMaker.name, workflowControls.name
|
||||
])
|
||||
.service('TemplatesService', templatesService)
|
||||
.service('WorkflowService', workflowService)
|
||||
.factory('WorkflowForm', WorkflowForm)
|
||||
// TODO: currently being kept arround for rbac selection, templates within projects and orgs, etc.
|
||||
.factory('TemplateList', TemplateList)
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
.WorkflowChart-linkHovering .WorkflowChart-linkOverlay {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
fill: @cgrey;
|
||||
fill: #E1E1E1;
|
||||
}
|
||||
|
||||
.WorkflowChart-linkHovering .WorkflowChart-linkPath {
|
||||
@ -27,9 +27,11 @@
|
||||
.WorkflowChart-link polygon,
|
||||
.WorkflowChart-link .WorkflowChart-betweenNodesIcon,
|
||||
.WorkflowChart-node .WorkflowChart-nodeAddCircle,
|
||||
.WorkflowChart-node .WorkflowChart-linkCircle,
|
||||
.WorkflowChart-node .WorkflowChart-nodeRemoveCircle,
|
||||
.WorkflowChart-node .WorkflowChart-nodeAddIcon,
|
||||
.WorkflowChart-node .WorkflowChart-nodeRemoveIcon {
|
||||
.WorkflowChart-node .WorkflowChart-nodeRemoveIcon,
|
||||
.WorkflowChart-node .WorkflowChart-nodeLinkIcon {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@ -41,6 +43,14 @@
|
||||
fill: @default-succ-hov;
|
||||
}
|
||||
|
||||
.WorkflowChart-node .WorkflowChart-linkCircle {
|
||||
fill: @default-link;
|
||||
}
|
||||
|
||||
.WorkflowChart-linkCircle.WorkflowChart-linkButtonHovering {
|
||||
fill: @default-link-hov;
|
||||
}
|
||||
|
||||
.WorkflowChart-node .WorkflowChart-nodeRemoveCircle {
|
||||
fill: @default-err;
|
||||
}
|
||||
@ -53,20 +63,27 @@
|
||||
fill: @default-secondary-bg;
|
||||
}
|
||||
|
||||
.WorkflowChart-rect.WorkflowChart-placeholder {
|
||||
.WorkflowChart-rect.WorkflowChart-isNodeBeingAdded {
|
||||
stroke-dasharray: 3;
|
||||
}
|
||||
|
||||
.WorkflowChart-node .WorkflowChart-transparentRect {
|
||||
.WorkflowChart-node .WorkflowChart-nodeOverlay--transparent {
|
||||
fill: @default-bg;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.WorkflowChart-node .WorkflowChart-nodeOverlay--disabled {
|
||||
fill: @default-dark;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.WorkflowChart-alwaysShowAdd circle,
|
||||
.WorkflowChart-alwaysShowAdd path,
|
||||
.WorkflowChart-alwaysShowAdd .WorkflowChart-betweenNodesIcon,
|
||||
.WorkflowChart-nodeHovering .WorkflowChart-nodeAddCircle,
|
||||
.WorkflowChart-nodeHovering .WorkflowChart-nodeAddIcon,
|
||||
.WorkflowChart-nodeHovering .WorkflowChart-linkCircle,
|
||||
.WorkflowChart-nodeHovering .WorkflowChart-nodeLinkIcon,
|
||||
.WorkflowChart-nodeHovering .WorkflowChart-nodeRemoveCircle,
|
||||
.WorkflowChart-nodeHovering .WorkflowChart-nodeRemoveIcon,
|
||||
.WorkflowChart-addHovering circle,
|
||||
@ -76,7 +93,7 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.WorkflowChart-link.WorkflowChart-placeholder {
|
||||
.WorkflowChart-link.WorkflowChart-isNodeBeingAdded {
|
||||
stroke-dasharray: 3;
|
||||
}
|
||||
|
||||
@ -184,3 +201,11 @@
|
||||
.WorkflowChart-dashedNode {
|
||||
stroke-dasharray: 5,5;
|
||||
}
|
||||
|
||||
.WorkflowChart-nodeLinkIcon {
|
||||
color: @default-bg;
|
||||
}
|
||||
|
||||
.WorkflowChart-nodeHovering .WorkflowChart-addLinkCircle {
|
||||
fill: @default-link;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -4,8 +4,8 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default ['$scope', 'TemplatesStrings', 'CreateSelect2', '$timeout',
|
||||
function($scope, TemplatesStrings, CreateSelect2, $timeout) {
|
||||
export default ['$scope', 'TemplatesStrings', 'CreateSelect2',
|
||||
function($scope, TemplatesStrings, CreateSelect2) {
|
||||
$scope.strings = TemplatesStrings;
|
||||
|
||||
$scope.edgeTypeOptions = [
|
||||
|
||||
@ -13,7 +13,8 @@ export default ['templateUrl',
|
||||
linkConfig: '<',
|
||||
readOnly: '<',
|
||||
cancel: '&',
|
||||
select: '&'
|
||||
select: '&',
|
||||
unlink: '&'
|
||||
},
|
||||
restrict: 'E',
|
||||
templateUrl: templateUrl('templates/workflows/workflow-maker/forms/workflow-link-form'),
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
<div class="WorkflowMaker-formTitle">{{readOnly ? strings.get('workflow_maker.VIEW_LINK', {parentName: linkConfig.parent.name, childName: linkConfig.child.name}) : strings.get('workflow_maker.EDIT_LINK', {parentName: linkConfig.parent.name, childName: linkConfig.child.name}) }}</div>
|
||||
<div class="WorkflowMaker-form">
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn">
|
||||
<div class="WorkflowMaker-formTitle">{{readOnly ? strings.get('workflow_maker.VIEW_LINK') : (linkConfig.mode === 'add' ? strings.get('workflow_maker.ADD_LINK') : strings.get('workflow_maker.EDIT_LINK')) }} | {{linkConfig.parent.name}} {{linkConfig.child ? 'to ' + linkConfig.child.name : ''}}</div>
|
||||
<div class="WorkflowMaker-form">
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn">
|
||||
<div ng-show="linkConfig.mode === 'add' && !linkConfig.child">{{:: strings.get('workflow_maker.NEW_LINK')}}</div>
|
||||
<span ng-show="linkConfig.child">
|
||||
<label for="edgeType" class="Form-inputLabelContainer">
|
||||
<span class="Form-requiredAsterisk">*</span>
|
||||
<span class="Form-inputLabel">{{:: strings.get('workflow_maker.RUN') }}</span>
|
||||
@ -17,11 +19,12 @@
|
||||
aria-hidden="true">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons Form-buttons" id="workflow_maker_controls">
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_link_btn" ng-show="!readOnly" ng-click="cancel()"> {{:: strings.get('CANCEL') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_link_btn" ng-show="readOnly" ng-click="cancel()"> {{:: strings.get('CLOSE') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_link_btn" ng-show="!readOnly" ng-click="select({edgeType: edgeType.value})" ng-disabled="!edgeType"> {{:: strings.get('SAVE') }}</button>
|
||||
</div>
|
||||
</span>
|
||||
</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_unlink_btn" ng-show="!readOnly && linkConfig.canUnlink" ng-click="unlink()"> {{:: strings.get('workflow_maker.UNLINK') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_link_btn" ng-show="!readOnly" ng-click="cancel()"> {{:: strings.get('CANCEL') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_link_btn" ng-show="readOnly" ng-click="cancel()"> {{:: strings.get('CLOSE') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_link_btn" ng-show="!readOnly && linkConfig.child" ng-click="select({edgeType: edgeType.value})" ng-disabled="!edgeType"> {{:: strings.get('SAVE') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
*************************************************/
|
||||
|
||||
export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService', 'Rest', '$q',
|
||||
'WorkflowService', 'TemplatesStrings', 'CreateSelect2', 'Empty', 'generateList', 'QuerySet',
|
||||
'GetBasePath', 'TemplateList', 'ProjectList', 'InventorySourcesList',
|
||||
'TemplatesStrings', 'CreateSelect2', 'Empty', 'generateList', 'QuerySet',
|
||||
'GetBasePath', 'TemplateList', 'ProjectList', 'InventorySourcesList', 'ProcessErrors',
|
||||
function($scope, TemplatesService, JobTemplate, PromptService, Rest, $q,
|
||||
WorkflowService, TemplatesStrings, CreateSelect2, Empty, generateList, qs,
|
||||
GetBasePath, TemplateList, ProjectList, InventorySourcesList
|
||||
TemplatesStrings, CreateSelect2, Empty, generateList, qs,
|
||||
GetBasePath, TemplateList, ProjectList, InventorySourcesList, ProcessErrors
|
||||
) {
|
||||
|
||||
let promptWatcher, credentialsWatcher, surveyQuestionWatcher, listPromises = [];
|
||||
@ -55,48 +55,6 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
projectList.disableRowValue = 'readOnly';
|
||||
$scope.projectList = projectList;
|
||||
|
||||
$scope.$watch('node', (newNode, oldNode) => {
|
||||
if (oldNode.id !== newNode.id) {
|
||||
setupNodeForm();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watchGroup(['templates', 'projects', 'inventory_sources', 'activeTab'], () => {
|
||||
// TODO: make this more concise
|
||||
switch($scope.activeTab) {
|
||||
case 'jobs':
|
||||
$scope.templates.forEach(function(row, i) {
|
||||
if(_.hasIn($scope, 'node.unifiedJobTemplate.id') && row.id === $scope.node.unifiedJobTemplate.id) {
|
||||
$scope.templates[i].checked = 1;
|
||||
}
|
||||
else {
|
||||
$scope.templates[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'project_syncs':
|
||||
$scope.projects.forEach(function(row, i) {
|
||||
if(_.hasIn($scope, 'node.unifiedJobTemplate.id') && row.id === $scope.node.unifiedJobTemplate.id) {
|
||||
$scope.projects[i].checked = 1;
|
||||
}
|
||||
else {
|
||||
$scope.projects[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'inventory_syncs':
|
||||
$scope.inventory_sources.forEach(function(row, i) {
|
||||
if(_.hasIn($scope, 'node.unifiedJobTemplate.id') && row.id === $scope.node.unifiedJobTemplate.id) {
|
||||
$scope.inventory_sources[i].checked = 1;
|
||||
}
|
||||
else {
|
||||
$scope.inventory_sources[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const checkCredentialsForRequiredPasswords = () => {
|
||||
let credentialRequiresPassword = false;
|
||||
$scope.promptData.prompts.credentials.value.forEach((credential) => {
|
||||
@ -135,14 +93,44 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
}
|
||||
};
|
||||
|
||||
const finishConfiguringAdd = () => {
|
||||
$scope.activeTab = "jobs";
|
||||
const alwaysOption = {
|
||||
label: $scope.strings.get('workflow_maker.ALWAYS'),
|
||||
value: 'always'
|
||||
};
|
||||
const successOption = {
|
||||
label: $scope.strings.get('workflow_maker.ON_SUCCESS'),
|
||||
value: 'success'
|
||||
};
|
||||
const failureOption = {
|
||||
label: $scope.strings.get('workflow_maker.ON_FAILURE'),
|
||||
value: 'failure'
|
||||
};
|
||||
$scope.edgeTypeOptions = [alwaysOption];
|
||||
switch($scope.nodeConfig.newNodeIsRoot) {
|
||||
case true:
|
||||
$scope.edgeType = alwaysOption;
|
||||
break;
|
||||
case false:
|
||||
$scope.edgeType = successOption;
|
||||
$scope.edgeTypeOptions.push(successOption, failureOption);
|
||||
break;
|
||||
}
|
||||
CreateSelect2({
|
||||
element: '#workflow_node_edge_3',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
$scope.nodeFormDataLoaded = true;
|
||||
};
|
||||
|
||||
const finishConfiguringEdit = () => {
|
||||
|
||||
let jobTemplate = new JobTemplate();
|
||||
|
||||
console.log($scope.node);
|
||||
|
||||
if (!_.isEmpty($scope.node.promptData)) {
|
||||
$scope.promptData = _.cloneDeep($scope.node.promptData);
|
||||
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 &&
|
||||
@ -162,7 +150,7 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'node.originalNodeObj.summary_fields.inventory')) {
|
||||
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;
|
||||
@ -170,13 +158,13 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
}
|
||||
$scope.nodeFormDataLoaded = true;
|
||||
} else if (
|
||||
_.get($scope, 'node.unifiedJobTemplate.unified_job_type') === 'job_template' ||
|
||||
_.get($scope, 'node.unifiedJobTemplate.type') === 'job_template'
|
||||
_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.unified_job_type') === 'job_template' ||
|
||||
_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.type') === 'job_template'
|
||||
) {
|
||||
let promises = [jobTemplate.optionsLaunch($scope.node.unifiedJobTemplate.id), jobTemplate.getLaunch($scope.node.unifiedJobTemplate.id)];
|
||||
let promises = [jobTemplate.optionsLaunch($scope.nodeConfig.node.fullUnifiedJobTemplateObject.id), jobTemplate.getLaunch($scope.nodeConfig.node.fullUnifiedJobTemplateObject.id)];
|
||||
|
||||
if (_.has($scope, 'node.originalNodeObj.related.credentials')) {
|
||||
Rest.setUrl($scope.node.originalNodeObj.related.credentials);
|
||||
if (_.has($scope, 'nodeConfig.node.originalNodeObject.related.credentials')) {
|
||||
Rest.setUrl($scope.nodeConfig.node.originalNodeObject.related.credentials);
|
||||
promises.push(Rest.get());
|
||||
}
|
||||
|
||||
@ -189,7 +177,7 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
let prompts = PromptService.processPromptValues({
|
||||
launchConf: responses[1].data,
|
||||
launchOptions: responses[0].data,
|
||||
currentValues: $scope.node.originalNodeObj
|
||||
currentValues: $scope.nodeConfig.node.originalNodeObject
|
||||
});
|
||||
|
||||
let defaultCredsWithoutOverrides = [];
|
||||
@ -222,7 +210,7 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
|
||||
prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
|
||||
|
||||
if ((!$scope.node.unifiedJobTemplate.inventory && !launchConf.ask_inventory_on_launch) || !$scope.node.unifiedJobTemplate.project) {
|
||||
if ((!$scope.nodeConfig.node.fullUnifiedJobTemplateObject.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeConfig.node.fullUnifiedJobTemplateObject.project) {
|
||||
$scope.selectedTemplateInvalid = true;
|
||||
} else {
|
||||
$scope.selectedTemplateInvalid = false;
|
||||
@ -264,7 +252,7 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'node.originalNodeObj.summary_fields.inventory')) {
|
||||
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;
|
||||
@ -272,24 +260,24 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
|
||||
if (responses[1].data.survey_enabled) {
|
||||
// go out and get the survey questions
|
||||
jobTemplate.getSurveyQuestions($scope.node.unifiedJobTemplate.id)
|
||||
jobTemplate.getSurveyQuestions($scope.nodeConfig.node.fullUnifiedJobTemplateObject.id)
|
||||
.then((surveyQuestionRes) => {
|
||||
|
||||
let processed = PromptService.processSurveyQuestions({
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
extra_data: _.cloneDeep($scope.node.originalNodeObj.extra_data)
|
||||
extra_data: _.cloneDeep($scope.nodeConfig.node.originalNodeObject.extra_data)
|
||||
});
|
||||
|
||||
$scope.missingSurveyValue = processed.missingSurveyValue;
|
||||
|
||||
$scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data);
|
||||
|
||||
$scope.node.promptData = $scope.promptData = {
|
||||
$scope.nodeConfig.node.promptData = $scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
template: $scope.node.unifiedJobTemplate.id
|
||||
template: $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id
|
||||
};
|
||||
|
||||
surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
|
||||
@ -309,11 +297,11 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
$scope.nodeFormDataLoaded = true;
|
||||
});
|
||||
} else {
|
||||
$scope.node.promptData = $scope.promptData = {
|
||||
$scope.nodeConfig.node.promptData = $scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
template: $scope.node.unifiedJobTemplate.id
|
||||
template: $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id
|
||||
};
|
||||
|
||||
checkCredentialsForRequiredPasswords();
|
||||
@ -328,12 +316,12 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
$scope.nodeFormDataLoaded = true;
|
||||
}
|
||||
|
||||
if (_.get($scope, 'node.unifiedJobTemplate')) {
|
||||
if (_.get($scope, 'node.unifiedJobTemplate.type') === "job_template") {
|
||||
if (_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject')) {
|
||||
if (_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.type') === "job_template") {
|
||||
$scope.activeTab = "jobs";
|
||||
}
|
||||
|
||||
$scope.selectedTemplate = $scope.node.unifiedJobTemplate;
|
||||
$scope.selectedTemplate = $scope.nodeConfig.node.fullUnifiedJobTemplateObject;
|
||||
|
||||
if ($scope.selectedTemplate.unified_job_type) {
|
||||
switch ($scope.selectedTemplate.unified_job_type) {
|
||||
@ -364,79 +352,6 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
$scope.activeTab = "jobs";
|
||||
}
|
||||
|
||||
if ($scope.mode === 'add') {
|
||||
const alwaysOption = {
|
||||
label: $scope.strings.get('workflow_maker.ALWAYS'),
|
||||
value: 'always'
|
||||
};
|
||||
const successOption = {
|
||||
label: $scope.strings.get('workflow_maker.ON_SUCCESS'),
|
||||
value: 'success'
|
||||
};
|
||||
const failureOption = {
|
||||
label: $scope.strings.get('workflow_maker.ON_FAILURE'),
|
||||
value: 'failure'
|
||||
};
|
||||
$scope.edgeTypeOptions = [alwaysOption];
|
||||
switch($scope.node.isRoot) {
|
||||
case true:
|
||||
$scope.edgeType = alwaysOption;
|
||||
break;
|
||||
case false:
|
||||
$scope.edgeType = successOption;
|
||||
$scope.edgeTypeOptions.push(successOption, failureOption);
|
||||
break;
|
||||
}
|
||||
CreateSelect2({
|
||||
element: '#workflow_node_edge_3',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
$scope.nodeFormDataLoaded = true;
|
||||
}
|
||||
};
|
||||
// Determine whether or not we need to go out and GET this nodes unified job template
|
||||
// in order to determine whether or not prompt fields are needed
|
||||
|
||||
$scope.openPromptModal = function() {
|
||||
$scope.promptData.triggerModalOpen = true;
|
||||
};
|
||||
|
||||
$scope.toggle_row = function(selectedRow) {
|
||||
if (!$scope.readOnly) {
|
||||
// TODO: make this more concise
|
||||
switch($scope.activeTab) {
|
||||
case 'jobs':
|
||||
$scope.templates.forEach(function(row, i) {
|
||||
if (row.id === selectedRow.id) {
|
||||
$scope.templates[i].checked = 1;
|
||||
|
||||
} else {
|
||||
$scope.templates[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'project_syncs':
|
||||
$scope.projects.forEach(function(row, i) {
|
||||
if (row.id === selectedRow.id) {
|
||||
$scope.projects[i].checked = 1;
|
||||
} else {
|
||||
$scope.projects[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'inventory_syncs':
|
||||
$scope.inventory_sources.forEach(function(row, i) {
|
||||
if (row.id === selectedRow.id) {
|
||||
$scope.inventory_sources[i].checked = 1;
|
||||
} else {
|
||||
$scope.inventory_sources[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
templateManuallySelected(selectedRow);
|
||||
}
|
||||
};
|
||||
|
||||
const templateManuallySelected = (selectedTemplate) => {
|
||||
@ -597,7 +512,7 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
page_size: '5',
|
||||
order_by: 'name',
|
||||
not__source: ''
|
||||
}
|
||||
};
|
||||
|
||||
$scope.inventory_sources = [];
|
||||
$scope.inventory_source_dataset = {};
|
||||
@ -612,26 +527,113 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService
|
||||
|
||||
$q.all(listPromises)
|
||||
.then(() => {
|
||||
if (!$scope.node.isNew && !$scope.node.edited && $scope.node.unifiedJobTemplate && $scope.node.unifiedJobTemplate.unified_job_type && $scope.node.unifiedJobTemplate.unified_job_type === 'job') {
|
||||
// This is a node that we got back from the api with an incomplete
|
||||
// unified job template so we're going to pull down the whole object
|
||||
|
||||
TemplatesService.getUnifiedJobTemplate($scope.node.unifiedJobTemplate.id)
|
||||
.then(function(data) {
|
||||
$scope.node.unifiedJobTemplate = _.clone(data.data.results[0]);
|
||||
finishConfiguringEdit();
|
||||
}, function(error) {
|
||||
ProcessErrors($scope, error.data, error.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get unified job template. GET returned ' +
|
||||
'status: ' + error.status
|
||||
if ($scope.nodeConfig.mode === "edit") {
|
||||
// Make sure that we have the full unified job template object
|
||||
if (!$scope.nodeConfig.node.fullUnifiedJobTemplate && _.get($scope, 'nodeConfig.node.originalNodeObject.summary_fields.unified_job_template.unified_job_type') === 'job') {
|
||||
// This is a node that we got back from the api with an incomplete
|
||||
// unified job template so we're going to pull down the whole object
|
||||
TemplatesService.getUnifiedJobTemplate($scope.nodeConfig.node.originalNodeObject.summary_fields.unified_job_template.id)
|
||||
.then(function({data}) {
|
||||
$scope.nodeConfig.node.fullUnifiedJobTemplateObject = data.results[0];
|
||||
finishConfiguringEdit();
|
||||
}, function(error) {
|
||||
ProcessErrors($scope, error.data, error.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get unified job template. GET returned ' +
|
||||
'status: ' + error.status
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
finishConfiguringEdit();
|
||||
}
|
||||
} else {
|
||||
finishConfiguringEdit();
|
||||
finishConfiguringAdd();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.openPromptModal = function() {
|
||||
$scope.promptData.triggerModalOpen = true;
|
||||
};
|
||||
|
||||
$scope.toggle_row = function(selectedRow) {
|
||||
if (!$scope.readOnly) {
|
||||
// TODO: make this more concise
|
||||
switch($scope.activeTab) {
|
||||
case 'jobs':
|
||||
$scope.templates.forEach(function(row, i) {
|
||||
if (row.id === selectedRow.id) {
|
||||
$scope.templates[i].checked = 1;
|
||||
|
||||
} else {
|
||||
$scope.templates[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'project_syncs':
|
||||
$scope.projects.forEach(function(row, i) {
|
||||
if (row.id === selectedRow.id) {
|
||||
$scope.projects[i].checked = 1;
|
||||
} else {
|
||||
$scope.projects[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'inventory_syncs':
|
||||
$scope.inventory_sources.forEach(function(row, i) {
|
||||
if (row.id === selectedRow.id) {
|
||||
$scope.inventory_sources[i].checked = 1;
|
||||
} else {
|
||||
$scope.inventory_sources[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
templateManuallySelected(selectedRow);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$watch('nodeConfig.nodeId', (newNodeId, oldNodeId) => {
|
||||
if (newNodeId !== oldNodeId) {
|
||||
setupNodeForm();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watchGroup(['templates', 'projects', 'inventory_sources', 'activeTab'], () => {
|
||||
// TODO: make this more concise
|
||||
switch($scope.activeTab) {
|
||||
case 'jobs':
|
||||
$scope.templates.forEach(function(row, i) {
|
||||
if(_.hasIn($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.id') && row.id === $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id) {
|
||||
$scope.templates[i].checked = 1;
|
||||
}
|
||||
else {
|
||||
$scope.templates[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'project_syncs':
|
||||
$scope.projects.forEach(function(row, i) {
|
||||
if(_.hasIn($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.id') && row.id === $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id) {
|
||||
$scope.projects[i].checked = 1;
|
||||
}
|
||||
else {
|
||||
$scope.projects[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'inventory_syncs':
|
||||
$scope.inventory_sources.forEach(function(row, i) {
|
||||
if(_.hasIn($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject.id') && row.id === $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id) {
|
||||
$scope.inventory_sources[i].checked = 1;
|
||||
}
|
||||
else {
|
||||
$scope.inventory_sources[i].checked = 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
setupNodeForm();
|
||||
}
|
||||
|
||||
@ -10,8 +10,7 @@ export default ['templateUrl',
|
||||
function(templateUrl) {
|
||||
return {
|
||||
scope: {
|
||||
mode: '<',
|
||||
node: '=',
|
||||
nodeConfig: '<',
|
||||
cancel: '&',
|
||||
select: '&',
|
||||
readOnly: '<'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div ng-show="nodeFormDataLoaded">
|
||||
<div class="WorkflowMaker-formTitle ng-binding">{{mode === 'edit' ? node.unifiedJobTemplate.name : strings.get('workflow_maker.ADD_A_TEMPLATE')}}</div>
|
||||
<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="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>
|
||||
@ -118,7 +118,7 @@
|
||||
<span>{{:: strings.get('workflows.CREDENTIAL_WITH_PASS') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-show="mode === 'add'">
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-show="nodeConfig.mode === 'add'">
|
||||
<label for="edgeType" class="Form-inputLabelContainer">
|
||||
<span class="Form-requiredAsterisk">*</span>
|
||||
<span class="Form-inputLabel">{{:: strings.get('workflow_maker.RUN') }}</span>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -74,26 +74,39 @@
|
||||
</div>
|
||||
<div class="WorkflowLegend-maker--right">
|
||||
<span class="WorkflowMaker-totalJobs">{{strings.get('workflow_maker.TOTAL_TEMPLATES')}}</span>
|
||||
<span class="badge List-titleBadge" ng-bind="treeData.data.totalNodes"></span>
|
||||
<span class="badge List-titleBadge" ng-bind="treeState.arrayOfNodesForChart.length-1"></span>
|
||||
<i ng-class="{'WorkflowMaker-manualControlsIcon--active': showManualControls}" class="fa fa-cog WorkflowMaker-manualControlsIcon" aria-hidden="true" alt="Controls" ng-click="toggleManualControls()"></i>
|
||||
<div ng-show="showManualControls" class="WorkflowMaker-manualControls noselect">
|
||||
<workflow-controls class="WorkflowControls" pan-chart="panChart(direction)" zoom-chart="zoomChart(zoom)" reset-chart="resetChart()" zoom-to-fit-chart="zoomToFitChart()"></workflow-controls>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<workflow-chart ng-if="modalOpen" tree-data="treeData.data" add-node="startAddNode(parent, betweenTwoNodes)" edit-node="startEditNode(nodeToEdit)" edit-link="startEditLink(parentId, childId)" delete-node="startDeleteNode(nodeToDelete)" workflow-zoomed="workflowZoomed(zoom)" can-add-workflow-job-template="canAddWorkflowJobTemplate" workflow-job-template-obj="workflowJobTemplateObj" mode="edit" class="WorkflowMaker-chart"></workflow-chart>
|
||||
<workflow-chart
|
||||
ng-if="modalOpen"
|
||||
tree-state="treeState"
|
||||
add-node-without-child="startAddNodeWithoutChild(parent)"
|
||||
add-node-with-child="startAddNodeWithChild(link)"
|
||||
edit-node="startEditNode(nodeToEdit)"
|
||||
edit-link="startEditLink(linkToEdit)"
|
||||
select-node-for-linking="selectNodeForLinking(nodeToStartLink)"
|
||||
delete-node="startDeleteNode(nodeToDelete)"
|
||||
workflow-zoomed="workflowZoomed(zoom)"
|
||||
read-only="readOnly"
|
||||
mode="edit"
|
||||
class="WorkflowMaker-chart">
|
||||
</workflow-chart>
|
||||
</div>
|
||||
<div class="WorkflowMaker-contentRight">
|
||||
<span ng-if="formState.showNodeForm">
|
||||
<workflow-node-form mode="nodeFormMode" node="nodeBeingWorkedOn" select="confirmNodeForm(selectedTemplate, promptData, edgeType)" cancel="cancelNodeForm()" read-only="!workflowJobTemplateObj.summary_fields.user_capabilities.edit"/>
|
||||
<workflow-node-form node-config="nodeConfig" node="nodeBeingWorkedOn" select="confirmNodeForm(selectedTemplate, promptData, edgeType)" cancel="cancelNodeForm()" read-only="!workflowJobTemplateObj.summary_fields.user_capabilities.edit"/>
|
||||
</span>
|
||||
<span ng-if="formState.showLinkForm">
|
||||
<workflow-link-form link-config="linkConfig" read-only="!workflowJobTemplateObj.summary_fields.user_capabilities.edit" select="confirmLinkForm(edgeType)" cancel="cancelLinkForm()"/>
|
||||
<workflow-link-form link-config="linkConfig" read-only="!workflowJobTemplateObj.summary_fields.user_capabilities.edit" select="confirmLinkForm(edgeType)" cancel="cancelLinkForm()" unlink="unlink()"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="WorkflowMaker-buttonHolder">
|
||||
<button type="button" class="btn btn-sm WorkflowMaker-cancelButton" ng-click="closeWorkflowMaker()"> {{:: strings.get('CLOSE') }}</button>
|
||||
<button type="button" class="btn btn-sm WorkflowMaker-saveButton" ng-click="saveWorkflowMaker()" ng-show="workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate" ng-disabled="showNodeForm || showLinkForm"> {{:: strings.get('SAVE') }}</button>
|
||||
<button type="button" class="btn btn-sm WorkflowMaker-saveButton" ng-click="saveWorkflowMaker()" ng-show="workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate" ng-disabled="formState.showNodeForm || formState.showLinkForm"> {{:: strings.get('SAVE') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,294 +0,0 @@
|
||||
export default ['$q', function($q){
|
||||
return {
|
||||
searchTree: function(params) {
|
||||
// params.element
|
||||
// params.matchingId
|
||||
// params.byNodeId
|
||||
|
||||
let prospectiveId = params.byNodeId ? params.element.nodeId : params.element.id;
|
||||
|
||||
if(prospectiveId === params.matchingId){
|
||||
return params.element;
|
||||
}else if (params.element.children && params.element.children.length > 0){
|
||||
let result = null;
|
||||
const thisService = this;
|
||||
_.forEach(params.element.children, function(child) {
|
||||
result = thisService.searchTree({
|
||||
element: child,
|
||||
matchingId: params.matchingId,
|
||||
byNodeId: params.byNodeId ? params.byNodeId : false
|
||||
});
|
||||
if(result) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
removeNodeFromTree: function(params) {
|
||||
// params.tree
|
||||
// params.nodeToBeDeleted
|
||||
|
||||
let parentNode = this.searchTree({
|
||||
element: params.tree,
|
||||
matchingId: params.nodeToBeDeleted.parent.id
|
||||
});
|
||||
let nodeToBeDeleted = this.searchTree({
|
||||
element: parentNode,
|
||||
matchingId: params.nodeToBeDeleted.id
|
||||
});
|
||||
|
||||
if(nodeToBeDeleted.children) {
|
||||
_.forEach(nodeToBeDeleted.children, function(child) {
|
||||
if(nodeToBeDeleted.isRoot) {
|
||||
child.isRoot = true;
|
||||
child.edgeType = "always";
|
||||
}
|
||||
child.parent = parentNode;
|
||||
parentNode.children.push(child);
|
||||
});
|
||||
}
|
||||
|
||||
_.forEach(parentNode.children, function(child, index) {
|
||||
if(child.id === params.nodeToBeDeleted.id) {
|
||||
parentNode.children.splice(index, 1);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
addPlaceholderNode: function(params) {
|
||||
// params.parent
|
||||
// params.betweenTwoNodes
|
||||
// params.tree
|
||||
// params.id
|
||||
|
||||
let placeholder = {
|
||||
children: [],
|
||||
c: "#D7D7D7",
|
||||
id: params.id,
|
||||
canDelete: true,
|
||||
canEdit: false,
|
||||
canAddTo: true,
|
||||
placeholder: true,
|
||||
isNew: true,
|
||||
edited: false,
|
||||
isRoot: (params.betweenTwoNodes) ? _.get(params, 'parent.source.isStartNode', false) : _.get(params, 'parent.isStartNode', false)
|
||||
};
|
||||
|
||||
let parentNode = (params.betweenTwoNodes) ? this.searchTree({element: params.tree, matchingId: params.parent.source.id}) : this.searchTree({element: params.tree, matchingId: params.parent.id});
|
||||
let placeholderRef;
|
||||
|
||||
if (params.betweenTwoNodes) {
|
||||
_.forEach(parentNode.children, function(child, index) {
|
||||
if (child.id === params.parent.target.id) {
|
||||
child.isRoot = false;
|
||||
placeholder.children.push(child);
|
||||
parentNode.children[index] = placeholder;
|
||||
placeholderRef = parentNode.children[index];
|
||||
child.parent = parentNode.children[index];
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (parentNode.children) {
|
||||
parentNode.children.push(placeholder);
|
||||
placeholderRef = parentNode.children[parentNode.children.length - 1];
|
||||
} else {
|
||||
parentNode.children = [placeholder];
|
||||
placeholderRef = parentNode.children[0];
|
||||
}
|
||||
}
|
||||
|
||||
return placeholderRef;
|
||||
},
|
||||
getSiblingConnectionTypes: function(params) {
|
||||
// params.parentId
|
||||
// params.childId
|
||||
// params.tree
|
||||
|
||||
let siblingConnectionTypes = {};
|
||||
|
||||
let parentNode = this.searchTree({
|
||||
element: params.tree,
|
||||
matchingId: params.parentId
|
||||
});
|
||||
|
||||
if(parentNode.children && parentNode.children.length > 0) {
|
||||
// Loop across them and add the types as keys to siblingConnectionTypes
|
||||
_.forEach(parentNode.children, function(child) {
|
||||
if(child.id !== params.childId && !child.placeholder && child.edgeType) {
|
||||
siblingConnectionTypes[child.edgeType] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Object.keys(siblingConnectionTypes);
|
||||
},
|
||||
buildTree: function(params) {
|
||||
//params.workflowNodes
|
||||
|
||||
let deferred = $q.defer();
|
||||
|
||||
let _this = this;
|
||||
|
||||
let treeData = {
|
||||
data: {
|
||||
id: 1,
|
||||
canDelete: false,
|
||||
canEdit: false,
|
||||
canAddTo: true,
|
||||
isStartNode: true,
|
||||
unifiedJobTemplate: {
|
||||
name: "Workflow Launch"
|
||||
},
|
||||
children: [],
|
||||
deletedNodes: [],
|
||||
totalNodes: 0
|
||||
},
|
||||
nextIndex: 2
|
||||
};
|
||||
|
||||
let nodesArray = params.workflowNodes;
|
||||
let nodesObj = {};
|
||||
let nonRootNodeIds = [];
|
||||
let allNodeIds = [];
|
||||
|
||||
// Determine which nodes are root nodes
|
||||
_.forEach(nodesArray, function(node) {
|
||||
nodesObj[node.id] = _.clone(node);
|
||||
|
||||
allNodeIds.push(node.id);
|
||||
|
||||
_.forEach(node.success_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
_.forEach(node.failure_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
_.forEach(node.always_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
});
|
||||
|
||||
let rootNodes = _.difference(allNodeIds, nonRootNodeIds);
|
||||
|
||||
// Loop across the root nodes and re-build the tree
|
||||
_.forEach(rootNodes, function(rootNodeId) {
|
||||
let branch = _this.buildBranch({
|
||||
nodeId: rootNodeId,
|
||||
edgeType: "always",
|
||||
nodesObj: nodesObj,
|
||||
isRoot: true,
|
||||
treeData: treeData
|
||||
});
|
||||
|
||||
treeData.data.children.push(branch);
|
||||
});
|
||||
|
||||
deferred.resolve(treeData);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
buildBranch: function(params) {
|
||||
// params.nodeId
|
||||
// params.parentId
|
||||
// params.edgeType
|
||||
// params.nodesObj
|
||||
// params.isRoot
|
||||
// params.treeData
|
||||
|
||||
let _this = this;
|
||||
|
||||
let treeNode = {
|
||||
children: [],
|
||||
c: "#D7D7D7",
|
||||
id: params.treeData.nextIndex,
|
||||
nodeId: params.nodeId,
|
||||
canDelete: true,
|
||||
canEdit: true,
|
||||
canAddTo: true,
|
||||
placeholder: false,
|
||||
edgeType: params.edgeType,
|
||||
isNew: false,
|
||||
edited: false,
|
||||
originalEdge: params.edgeType,
|
||||
originalNodeObj: _.clone(params.nodesObj[params.nodeId]),
|
||||
promptValues: {},
|
||||
isRoot: params.isRoot ? params.isRoot : false
|
||||
};
|
||||
|
||||
params.treeData.data.totalNodes++;
|
||||
|
||||
params.treeData.nextIndex++;
|
||||
|
||||
if(params.parentId) {
|
||||
treeNode.originalParentId = params.parentId;
|
||||
}
|
||||
|
||||
if(params.nodesObj[params.nodeId].summary_fields) {
|
||||
if(params.nodesObj[params.nodeId].summary_fields.job) {
|
||||
treeNode.job = _.clone(params.nodesObj[params.nodeId].summary_fields.job);
|
||||
}
|
||||
|
||||
if(params.nodesObj[params.nodeId].summary_fields.unified_job_template) {
|
||||
treeNode.unifiedJobTemplate = _.clone(params.nodesObj[params.nodeId].summary_fields.unified_job_template);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop across the success nodes and add them recursively
|
||||
_.forEach(params.nodesObj[params.nodeId].success_nodes, function(successNodeId) {
|
||||
treeNode.children.push(_this.buildBranch({
|
||||
nodeId: successNodeId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "success",
|
||||
nodesObj: params.nodesObj,
|
||||
treeData: params.treeData
|
||||
}));
|
||||
});
|
||||
|
||||
// failure nodes
|
||||
_.forEach(params.nodesObj[params.nodeId].failure_nodes, function(failureNodesId) {
|
||||
treeNode.children.push(_this.buildBranch({
|
||||
nodeId: failureNodesId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "failure",
|
||||
nodesObj: params.nodesObj,
|
||||
treeData: params.treeData
|
||||
}));
|
||||
});
|
||||
|
||||
// always nodes
|
||||
_.forEach(params.nodesObj[params.nodeId].always_nodes, function(alwaysNodesId) {
|
||||
treeNode.children.push(_this.buildBranch({
|
||||
nodeId: alwaysNodesId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "always",
|
||||
nodesObj: params.nodesObj,
|
||||
treeData: params.treeData
|
||||
}));
|
||||
});
|
||||
|
||||
return treeNode;
|
||||
},
|
||||
updateStatusOfNode: function(params) {
|
||||
// params.treeData
|
||||
// params.nodeId
|
||||
// params.status
|
||||
|
||||
let matchingNode = this.searchTree({
|
||||
element: params.treeData.data,
|
||||
matchingId: params.nodeId,
|
||||
byNodeId: true
|
||||
});
|
||||
|
||||
if(matchingNode) {
|
||||
matchingNode.job = {
|
||||
status: params.status,
|
||||
id: params.unified_job_id
|
||||
};
|
||||
}
|
||||
|
||||
},
|
||||
};
|
||||
}];
|
||||
@ -1,10 +1,13 @@
|
||||
export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
|
||||
'jobLabels', 'workflowNodes', '$scope', 'ParseTypeChange',
|
||||
'ParseVariableString', 'WorkflowService', 'count', '$state', 'i18n',
|
||||
'moment', '$filter', function(workflowData, workflowResultsService,
|
||||
'ParseVariableString', 'count', '$state', 'i18n',
|
||||
'moment', function(workflowData, workflowResultsService,
|
||||
workflowDataOptions, jobLabels, workflowNodes, $scope, ParseTypeChange,
|
||||
ParseVariableString, WorkflowService, count, $state, i18n, moment, $filter) {
|
||||
ParseVariableString, count, $state, i18n, moment) {
|
||||
var runTimeElapsedTimer = null;
|
||||
let workflowMakerNodeIdCounter = 1;
|
||||
let nodeIdToMakerIdMapping = {};
|
||||
let chartNodeIdToIndexMapping = {};
|
||||
|
||||
var getLinks = function() {
|
||||
var getLink = function(key) {
|
||||
@ -113,11 +116,8 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
|
||||
$scope.workflow_nodes = workflowNodes;
|
||||
$scope.workflowOptions = workflowDataOptions.actions.GET;
|
||||
$scope.labels = jobLabels;
|
||||
$scope.count = count.val;
|
||||
$scope.showManualControls = false;
|
||||
$scope.showKey = false;
|
||||
$scope.toggleKey = () => $scope.showKey = !$scope.showKey;
|
||||
$scope.keyClassList = `{ 'Key-menuIcon--active': showKey }`;
|
||||
$scope.readOnly = true;
|
||||
|
||||
// Start elapsed time updater for job known to be running
|
||||
if ($scope.workflow.started !== null && $scope.workflow.status === 'running') {
|
||||
@ -167,25 +167,96 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
|
||||
$scope.varsTooltip= i18n._('Read only view of extra variables added to the workflow.');
|
||||
$scope.varsLabel = i18n._('Extra Variables');
|
||||
|
||||
|
||||
// Click binding for the expand/collapse button on the standard out log
|
||||
$scope.stdoutFullScreen = false;
|
||||
|
||||
WorkflowService.buildTree({
|
||||
workflowNodes: workflowNodes
|
||||
}).then(function(data){
|
||||
$scope.treeData = data;
|
||||
|
||||
// TODO: I think that the workflow chart directive (and eventually d3) is meddling with
|
||||
// this treeData object and removing the children object for some reason (?)
|
||||
// This happens on occasion and I think is a race condition (?)
|
||||
if(!$scope.treeData.data.children) {
|
||||
$scope.treeData.data.children = [];
|
||||
let nonRootNodeIds = [];
|
||||
let allNodeIds = [];
|
||||
let arrayOfLinksForChart = [];
|
||||
let arrayOfNodesForChart = [
|
||||
{
|
||||
index: 0,
|
||||
id: workflowMakerNodeIdCounter,
|
||||
isStartNode: true,
|
||||
unifiedJobTemplate: {
|
||||
name: "START"
|
||||
},
|
||||
fixed: true,
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
];
|
||||
|
||||
$scope.canAddWorkflowJobTemplate = false;
|
||||
workflowMakerNodeIdCounter++;
|
||||
// Assign each node an ID - 0 is reserved for the start node. We need to
|
||||
// make sure that we have an ID on every node including new nodes so the
|
||||
// ID returned by the api won't do
|
||||
workflowNodes.forEach((node) => {
|
||||
node.workflowMakerNodeId = workflowMakerNodeIdCounter;
|
||||
const nodeObj = {
|
||||
index: workflowMakerNodeIdCounter-1,
|
||||
id: workflowMakerNodeIdCounter,
|
||||
unifiedJobTemplate: node.summary_fields.unified_job_template
|
||||
};
|
||||
if(node.summary_fields.job) {
|
||||
nodeObj.job = node.summary_fields.job;
|
||||
}
|
||||
if(node.summary_fields.unified_job_template) {
|
||||
nodeObj.unifiedJobTemplate = node.summary_fields.unified_job_template;
|
||||
}
|
||||
arrayOfNodesForChart.push(nodeObj);
|
||||
allNodeIds.push(node.id);
|
||||
nodeIdToMakerIdMapping[node.id] = node.workflowMakerNodeId;
|
||||
chartNodeIdToIndexMapping[workflowMakerNodeIdCounter] = workflowMakerNodeIdCounter-1;
|
||||
workflowMakerNodeIdCounter++;
|
||||
});
|
||||
|
||||
workflowNodes.forEach((node) => {
|
||||
const sourceIndex = chartNodeIdToIndexMapping[node.workflowMakerNodeId];
|
||||
node.success_nodes.forEach((nodeId) => {
|
||||
const targetIndex = chartNodeIdToIndexMapping[nodeIdToMakerIdMapping[nodeId]];
|
||||
arrayOfLinksForChart.push({
|
||||
source: arrayOfNodesForChart[sourceIndex],
|
||||
target: arrayOfNodesForChart[targetIndex],
|
||||
edgeType: "success"
|
||||
});
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
node.failure_nodes.forEach((nodeId) => {
|
||||
const targetIndex = chartNodeIdToIndexMapping[nodeIdToMakerIdMapping[nodeId]];
|
||||
arrayOfLinksForChart.push({
|
||||
source: arrayOfNodesForChart[sourceIndex],
|
||||
target: arrayOfNodesForChart[targetIndex],
|
||||
edgeType: "failure"
|
||||
});
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
node.always_nodes.forEach((nodeId) => {
|
||||
const targetIndex = chartNodeIdToIndexMapping[nodeIdToMakerIdMapping[nodeId]];
|
||||
arrayOfLinksForChart.push({
|
||||
source: arrayOfNodesForChart[sourceIndex],
|
||||
target: arrayOfNodesForChart[targetIndex],
|
||||
edgeType: "always"
|
||||
});
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
});
|
||||
|
||||
let uniqueNonRootNodeIds = Array.from(new Set(nonRootNodeIds));
|
||||
|
||||
let rootNodes = _.difference(allNodeIds, uniqueNonRootNodeIds);
|
||||
|
||||
rootNodes.forEach((rootNodeId) => {
|
||||
const targetIndex = chartNodeIdToIndexMapping[nodeIdToMakerIdMapping[rootNodeId]];
|
||||
arrayOfLinksForChart.push({
|
||||
source: arrayOfNodesForChart[0],
|
||||
target: arrayOfNodesForChart[targetIndex],
|
||||
edgeType: "always"
|
||||
});
|
||||
});
|
||||
|
||||
$scope.treeState = { arrayOfNodesForChart, arrayOfLinksForChart };
|
||||
|
||||
}
|
||||
|
||||
$scope.toggleStdoutFullscreen = function() {
|
||||
@ -285,12 +356,11 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
|
||||
runTimeElapsedTimer = workflowResultsService.createOneSecondTimer(moment(), updateWorkflowJobElapsedTimer);
|
||||
}
|
||||
|
||||
WorkflowService.updateStatusOfNode({
|
||||
treeData: $scope.treeData,
|
||||
nodeId: data.workflow_node_id,
|
||||
status: data.status,
|
||||
unified_job_id: data.unified_job_id
|
||||
});
|
||||
$scope.treeState.arrayOfNodesForChart[chartNodeIdToIndexMapping[nodeIdToMakerIdMapping[data.workflow_node_id]]].job = {
|
||||
id: data.unified_job_id,
|
||||
status: data.status
|
||||
};
|
||||
|
||||
|
||||
$scope.workflow_nodes.forEach(node => {
|
||||
if(parseInt(node.id) === parseInt(data.workflow_node_id)){
|
||||
@ -300,8 +370,6 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
|
||||
}
|
||||
});
|
||||
|
||||
$scope.count = workflowResultsService
|
||||
.getCounts($scope.workflow_nodes);
|
||||
$scope.$broadcast("refreshWorkflowChart");
|
||||
}
|
||||
getLabelsAndTooltips();
|
||||
|
||||
@ -363,7 +363,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<workflow-chart tree-data="treeData.data" workflow-zoomed="workflowZoomed(zoom)" can-add-workflow-job-template="canAddWorkflowJobTemplate" mode="details" class="WorkflowMaker-chart"></workflow-chart>
|
||||
<workflow-chart
|
||||
tree-state="treeState"
|
||||
workflow-zoomed="workflowZoomed(zoom)"
|
||||
can-add-workflow-job-template="canAddWorkflowJobTemplate"
|
||||
mode="details"
|
||||
read-only="readOnly"
|
||||
class="WorkflowMaker-chart">
|
||||
</workflow-chart>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user