mirror of
https://github.com/ansible/awx.git
synced 2026-01-16 04:10:44 -03:30
Merge pull request #4389 from mabashian/4165-add-edit-node-audit
Add/Edit workflow from audit items
This commit is contained in:
commit
e00e4f6a10
@ -170,7 +170,7 @@ export default
|
||||
ngClick: 'cancelNodeForm()',
|
||||
ngShow: '!canAddWorkflowJobTemplate'
|
||||
},
|
||||
save: {
|
||||
select: {
|
||||
ngClick: 'saveNodeForm()',
|
||||
ngDisabled: "workflow_maker_form.$invalid || !selectedTemplate",
|
||||
ngShow: 'canAddWorkflowJobTemplate'
|
||||
|
||||
@ -16,9 +16,10 @@ export default
|
||||
.factory('WorkflowFormObject', ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
addTitle: i18n._('New Workflow'),
|
||||
addTitle: i18n._('New Workflow Job Template'),
|
||||
editTitle: '{{ name }}',
|
||||
name: 'workflow_job_template',
|
||||
breadcrumbName: i18n._('WORKFLOW'),
|
||||
base: 'workflow',
|
||||
basePath: 'workflow_job_templates',
|
||||
// the top-most node of generated state tree
|
||||
|
||||
@ -1687,6 +1687,10 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
button.label = i18n._('Save');
|
||||
button['class'] = 'Form-saveButton';
|
||||
}
|
||||
if (btn === 'select') {
|
||||
button.label = i18n._('Select');
|
||||
button['class'] = 'Form-saveButton';
|
||||
}
|
||||
if (btn === 'cancel') {
|
||||
button.label = i18n._('Cancel');
|
||||
button['class'] = 'Form-cancelButton';
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button ng-click="cancelForm()" class="Lookup-cancel btn btn-default">Cancel</button>
|
||||
<button ng-click="saveForm()" class="Lookup-save btn btn-primary">Save</button>
|
||||
<button ng-click="saveForm()" class="Lookup-save btn btn-primary" ng-bind="list.lookupConfirmText ? list.lookupConfirmText : 'Save'"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -401,8 +401,10 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesA
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
ListDefinition: ['InventoryList', function(list) {
|
||||
ListDefinition: ['InventoryList', function(InventoryList) {
|
||||
// mutate the provided list definition here
|
||||
let list = _.cloneDeep(InventoryList);
|
||||
list.lookupConfirmText = 'SELECT';
|
||||
return list;
|
||||
}],
|
||||
Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
@ -451,8 +453,9 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesA
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
ListDefinition: ['CredentialList', function(list) {
|
||||
// mutate the provided list definition here
|
||||
ListDefinition: ['CredentialList', function(CredentialList) {
|
||||
let list = _.cloneDeep(CredentialList);
|
||||
list.lookupConfirmText = 'SELECT';
|
||||
return list;
|
||||
}],
|
||||
Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
|
||||
@ -33,10 +33,6 @@
|
||||
$scope.parseType = 'yaml';
|
||||
$scope.includeWorkflowMaker = false;
|
||||
|
||||
$scope.editRequests = [];
|
||||
$scope.associateRequests = [];
|
||||
$scope.disassociateRequests = [];
|
||||
|
||||
function init() {
|
||||
|
||||
// Select2-ify the lables input
|
||||
@ -111,33 +107,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
// Get the workflow nodes
|
||||
TemplatesService.getWorkflowJobTemplateNodes(id)
|
||||
.then(function(data){
|
||||
|
||||
$scope.workflowTree = WorkflowService.buildTree({
|
||||
workflowNodes: data.data.results
|
||||
});
|
||||
|
||||
// TODO: I think that the workflow chart directive (and eventually d3) is meddling with
|
||||
// this workflowTree object and removing the children object for some reason (?)
|
||||
// This happens on occasion and I think is a race condition (?)
|
||||
if(!$scope.workflowTree.data.children) {
|
||||
$scope.workflowTree.data.children = [];
|
||||
}
|
||||
|
||||
// In the partial, the workflow maker directive has an ng-if attribute which is pointed at this scope variable.
|
||||
// It won't get included until this the tree has been built - I'm open to better ways of doing this.
|
||||
$scope.includeWorkflowMaker = true;
|
||||
|
||||
}, function(error){
|
||||
ProcessErrors($scope, error.data, error.status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get workflow job template nodes. GET returned ' +
|
||||
'status: ' + error.status
|
||||
});
|
||||
});
|
||||
|
||||
// Go out and GET the workflow job temlate data needed to populate the form
|
||||
TemplatesService.getWorkflowJobTemplate(id)
|
||||
.then(function(data){
|
||||
@ -180,6 +149,35 @@
|
||||
$scope.url = workflowJobTemplateData.url;
|
||||
$scope.survey_enabled = workflowJobTemplateData.survey_enabled;
|
||||
|
||||
// Get the workflow nodes
|
||||
TemplatesService.getWorkflowJobTemplateNodes(id)
|
||||
.then(function(data){
|
||||
|
||||
$scope.workflowTree = WorkflowService.buildTree({
|
||||
workflowNodes: data.data.results
|
||||
});
|
||||
|
||||
// TODO: I think that the workflow chart directive (and eventually d3) is meddling with
|
||||
// this workflowTree object and removing the children object for some reason (?)
|
||||
// This happens on occasion and I think is a race condition (?)
|
||||
if(!$scope.workflowTree.data.children) {
|
||||
$scope.workflowTree.data.children = [];
|
||||
}
|
||||
|
||||
$scope.workflowTree.workflow_job_template_obj = $scope.workflow_job_template_obj;
|
||||
|
||||
// In the partial, the workflow maker directive has an ng-if attribute which is pointed at this scope variable.
|
||||
// It won't get included until this the tree has been built - I'm open to better ways of doing this.
|
||||
$scope.includeWorkflowMaker = true;
|
||||
|
||||
}, function(error){
|
||||
ProcessErrors($scope, error.data, error.status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get workflow job template nodes. GET returned ' +
|
||||
'status: ' + error.status
|
||||
});
|
||||
});
|
||||
|
||||
}, function(error){
|
||||
ProcessErrors($scope, error.data, error.status, form, {
|
||||
hdr: 'Error!',
|
||||
@ -189,160 +187,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
function recursiveNodeUpdates(params, completionCallback) {
|
||||
// params.parentId
|
||||
// params.node
|
||||
|
||||
let generatePostUrl = function(){
|
||||
|
||||
let base = (params.parentId) ? GetBasePath('workflow_job_template_nodes') + params.parentId : $scope.workflow_job_template_obj.related.workflow_nodes;
|
||||
|
||||
if(params.parentId) {
|
||||
if(params.node.edgeType === 'success') {
|
||||
base += "/success_nodes";
|
||||
}
|
||||
else if(params.node.edgeType === 'failure') {
|
||||
base += "/failure_nodes";
|
||||
}
|
||||
else if(params.node.edgeType === 'always') {
|
||||
base += "/always_nodes";
|
||||
}
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
};
|
||||
|
||||
let buildSendableNodeData = function() {
|
||||
// Create the node
|
||||
let sendableNodeData = {
|
||||
unified_job_template: params.node.unifiedJobTemplate.id
|
||||
};
|
||||
|
||||
// Check to see if the user has provided any prompt values that are different
|
||||
// from the defaults in the job template
|
||||
|
||||
if(params.node.unifiedJobTemplate.type === "job_template" && params.node.promptValues) {
|
||||
if(params.node.unifiedJobTemplate.ask_credential_on_launch) {
|
||||
sendableNodeData.credential = !params.node.promptValues.credential || params.node.unifiedJobTemplate.summary_fields.credential.id !== params.node.promptValues.credential.id ? params.node.promptValues.credential.id : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_inventory_on_launch) {
|
||||
sendableNodeData.inventory = !params.node.promptValues.inventory || params.node.unifiedJobTemplate.summary_fields.inventory.id !== params.node.promptValues.inventory.id ? params.node.promptValues.inventory.id : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_limit_on_launch) {
|
||||
sendableNodeData.limit = !params.node.promptValues.limit || params.node.unifiedJobTemplate.limit !== params.node.promptValues.limit ? params.node.promptValues.limit : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_job_type_on_launch) {
|
||||
sendableNodeData.job_type = !params.node.promptValues.job_type || params.node.unifiedJobTemplate.job_type !== params.node.promptValues.job_type ? params.node.promptValues.job_type : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_tags_on_launch) {
|
||||
sendableNodeData.job_tags = !params.node.promptValues.job_tags || params.node.unifiedJobTemplate.job_tags !== params.node.promptValues.job_tags ? params.node.promptValues.job_tags : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_skip_tags_on_launch) {
|
||||
sendableNodeData.skip_tags = !params.node.promptValues.skip_tags || params.node.unifiedJobTemplate.skip_tags !== params.node.promptValues.skip_tags ? params.node.promptValues.skip_tags : null;
|
||||
}
|
||||
}
|
||||
|
||||
return sendableNodeData;
|
||||
};
|
||||
|
||||
let continueRecursing = function(parentId) {
|
||||
$scope.totalIteratedNodes++;
|
||||
|
||||
if($scope.totalIteratedNodes === $scope.workflowTree.data.totalNodes) {
|
||||
// We're done recursing, lets move on
|
||||
completionCallback();
|
||||
}
|
||||
else {
|
||||
if(params.node.children && params.node.children.length > 0) {
|
||||
_.forEach(params.node.children, function(child) {
|
||||
if(child.edgeType === "success") {
|
||||
recursiveNodeUpdates({
|
||||
parentId: parentId,
|
||||
node: child
|
||||
}, completionCallback);
|
||||
}
|
||||
else if(child.edgeType === "failure") {
|
||||
recursiveNodeUpdates({
|
||||
parentId: parentId,
|
||||
node: child
|
||||
}, completionCallback);
|
||||
}
|
||||
else if(child.edgeType === "always") {
|
||||
recursiveNodeUpdates({
|
||||
parentId: parentId,
|
||||
node: child
|
||||
}, completionCallback);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(params.node.isNew) {
|
||||
|
||||
TemplatesService.addWorkflowNode({
|
||||
url: generatePostUrl(),
|
||||
data: buildSendableNodeData()
|
||||
})
|
||||
.then(function(data) {
|
||||
continueRecursing(data.data.id);
|
||||
}, function(error) {
|
||||
ProcessErrors($scope, error.data, error.status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add workflow node. ' +
|
||||
'POST returned status: ' +
|
||||
error.status
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
if(params.node.edited || !params.node.originalParentId || (params.node.originalParentId && params.parentId !== params.node.originalParentId)) {
|
||||
|
||||
if(params.node.edited) {
|
||||
|
||||
$scope.editRequests.push({
|
||||
id: params.node.nodeId,
|
||||
data: buildSendableNodeData()
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep
|
||||
|
||||
$scope.disassociateRequests.push({
|
||||
parentId: params.node.originalParentId,
|
||||
nodeId: params.node.nodeId,
|
||||
edge: params.node.originalEdge
|
||||
});
|
||||
|
||||
// Can only associate if we have a parent.
|
||||
// If we don't have a parent then this is a root node
|
||||
// and the act of disassociating will make it a root node
|
||||
if(params.parentId) {
|
||||
$scope.associateRequests.push({
|
||||
parentId: params.parentId,
|
||||
nodeId: params.node.nodeId,
|
||||
edge: params.node.edgeType
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
else if(!params.node.originalParentId && params.parentId) {
|
||||
// This used to be a root node but is now not a root node
|
||||
$scope.associateRequests.push({
|
||||
parentId: params.parentId,
|
||||
nodeId: params.node.nodeId,
|
||||
edge: params.node.edgeType
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
continueRecursing(params.node.nodeId);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.openWorkflowMaker = function() {
|
||||
$state.go('.workflowMaker');
|
||||
};
|
||||
@ -392,231 +236,97 @@
|
||||
.filter("[data-label-is-present=true]")
|
||||
.map((i, val) => ({name: $(val).text()}));
|
||||
|
||||
$scope.totalIteratedNodes = 0;
|
||||
TemplatesService.updateWorkflowJobTemplate({
|
||||
id: id,
|
||||
data: data
|
||||
}).then(function(){
|
||||
|
||||
// TODO: this is the only way that I could figure out to get
|
||||
// these promise arrays to play nicely. I tried to just append
|
||||
// a single promise to deletePromises but it just wasn't working
|
||||
let editWorkflowJobTemplate = [id].map(function(id) {
|
||||
return TemplatesService.updateWorkflowJobTemplate({
|
||||
id: id,
|
||||
data: data
|
||||
});
|
||||
});
|
||||
var orgDefer = $q.defer();
|
||||
var associationDefer = $q.defer();
|
||||
var associatedLabelsDefer = $q.defer();
|
||||
|
||||
if($scope.workflowTree && $scope.workflowTree.data && $scope.workflowTree.data.children && $scope.workflowTree.data.children.length > 0) {
|
||||
let completionCallback = function() {
|
||||
|
||||
let disassociatePromises = $scope.disassociateRequests.map(function(request) {
|
||||
return TemplatesService.disassociateWorkflowNode({
|
||||
parentId: request.parentId,
|
||||
nodeId: request.nodeId,
|
||||
edge: request.edge
|
||||
var getNext = function(data, arr, resolve) {
|
||||
Rest.setUrl(data.next);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
if (data.next) {
|
||||
getNext(data, arr.concat(data.results), resolve);
|
||||
} else {
|
||||
resolve.resolve(arr.concat(data.results));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let editNodePromises = $scope.editRequests.map(function(request) {
|
||||
return TemplatesService.editWorkflowNode({
|
||||
id: request.id,
|
||||
data: request.data
|
||||
});
|
||||
});
|
||||
|
||||
$q.all(disassociatePromises.concat(editNodePromises).concat(editWorkflowJobTemplate))
|
||||
.then(function() {
|
||||
|
||||
let associatePromises = $scope.associateRequests.map(function(request) {
|
||||
return TemplatesService.associateWorkflowNode({
|
||||
parentId: request.parentId,
|
||||
nodeId: request.nodeId,
|
||||
edge: request.edge
|
||||
});
|
||||
});
|
||||
|
||||
let deletePromises = $scope.workflowTree.data.deletedNodes.map(function(nodeId) {
|
||||
return TemplatesService.deleteWorkflowJobTemplateNode(nodeId);
|
||||
});
|
||||
|
||||
$q.all(associatePromises.concat(deletePromises))
|
||||
.then(function() {
|
||||
|
||||
var orgDefer = $q.defer();
|
||||
var associationDefer = $q.defer();
|
||||
var associatedLabelsDefer = $q.defer();
|
||||
|
||||
var getNext = function(data, arr, resolve) {
|
||||
Rest.setUrl(data.next);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
if (data.next) {
|
||||
getNext(data, arr.concat(data.results), resolve);
|
||||
} else {
|
||||
resolve.resolve(arr.concat(data.results));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Rest.setUrl($scope.workflow_job_template_obj.related.labels);
|
||||
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
if (data.next) {
|
||||
getNext(data, data.results, associatedLabelsDefer);
|
||||
} else {
|
||||
associatedLabelsDefer.resolve(data.results);
|
||||
}
|
||||
});
|
||||
|
||||
associatedLabelsDefer.promise.then(function (current) {
|
||||
current = current.map(data => data.id);
|
||||
var labelsToAdd = $scope.labels
|
||||
.map(val => val.value);
|
||||
var labelsToDisassociate = current
|
||||
.filter(val => labelsToAdd
|
||||
.indexOf(val) === -1)
|
||||
.map(val => ({id: val, disassociate: true}));
|
||||
var labelsToAssociate = labelsToAdd
|
||||
.filter(val => current
|
||||
.indexOf(val) === -1)
|
||||
.map(val => ({id: val, associate: true}));
|
||||
var pass = labelsToDisassociate
|
||||
.concat(labelsToAssociate);
|
||||
associationDefer.resolve(pass);
|
||||
});
|
||||
|
||||
Rest.setUrl(GetBasePath("organizations"));
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
orgDefer.resolve(data.results[0].id);
|
||||
});
|
||||
|
||||
orgDefer.promise.then(function(orgId) {
|
||||
var toPost = [];
|
||||
$scope.newLabels = $scope.newLabels
|
||||
.map(function(i, val) {
|
||||
val.organization = orgId;
|
||||
return val;
|
||||
});
|
||||
|
||||
$scope.newLabels.each(function(i, val) {
|
||||
toPost.push(val);
|
||||
});
|
||||
|
||||
associationDefer.promise.then(function(arr) {
|
||||
toPost = toPost
|
||||
.concat(arr);
|
||||
|
||||
Rest.setUrl($scope.workflow_job_template_obj.related.labels);
|
||||
|
||||
var defers = [];
|
||||
for (var i = 0; i < toPost.length; i++) {
|
||||
defers.push(Rest.post(toPost[i]));
|
||||
}
|
||||
$q.all(defers)
|
||||
.then(function() {
|
||||
$state.go('templates.editWorkflowJobTemplate', {id: id}, {reload: true});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_.forEach($scope.workflowTree.data.children, function(child) {
|
||||
recursiveNodeUpdates({
|
||||
node: child
|
||||
}, completionCallback);
|
||||
});
|
||||
}
|
||||
else {
|
||||
Rest.setUrl($scope.workflow_job_template_obj.related.labels);
|
||||
|
||||
let deletePromises = $scope.workflowTree.data.deletedNodes.map(function(nodeId) {
|
||||
return TemplatesService.deleteWorkflowJobTemplateNode(nodeId);
|
||||
});
|
||||
|
||||
$q.all(deletePromises.concat(editWorkflowJobTemplate))
|
||||
.then(function() {
|
||||
var orgDefer = $q.defer();
|
||||
var associationDefer = $q.defer();
|
||||
var associatedLabelsDefer = $q.defer();
|
||||
|
||||
var getNext = function(data, arr, resolve) {
|
||||
Rest.setUrl(data.next);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
if (data.next) {
|
||||
getNext(data, arr.concat(data.results), resolve);
|
||||
} else {
|
||||
resolve.resolve(arr.concat(data.results));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Rest.setUrl($scope.workflow_job_template_obj.related.labels);
|
||||
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
if (data.next) {
|
||||
getNext(data, data.results, associatedLabelsDefer);
|
||||
} else {
|
||||
associatedLabelsDefer.resolve(data.results);
|
||||
}
|
||||
});
|
||||
|
||||
associatedLabelsDefer.promise.then(function (current) {
|
||||
current = current.map(data => data.id);
|
||||
var labelsToAdd = $scope.labels
|
||||
.map(val => val.value);
|
||||
var labelsToDisassociate = current
|
||||
.filter(val => labelsToAdd
|
||||
.indexOf(val) === -1)
|
||||
.map(val => ({id: val, disassociate: true}));
|
||||
var labelsToAssociate = labelsToAdd
|
||||
.filter(val => current
|
||||
.indexOf(val) === -1)
|
||||
.map(val => ({id: val, associate: true}));
|
||||
var pass = labelsToDisassociate
|
||||
.concat(labelsToAssociate);
|
||||
associationDefer.resolve(pass);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
if (data.next) {
|
||||
getNext(data, data.results, associatedLabelsDefer);
|
||||
} else {
|
||||
associatedLabelsDefer.resolve(data.results);
|
||||
}
|
||||
});
|
||||
|
||||
Rest.setUrl(GetBasePath("organizations"));
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
orgDefer.resolve(data.results[0].id);
|
||||
associatedLabelsDefer.promise.then(function (current) {
|
||||
current = current.map(data => data.id);
|
||||
var labelsToAdd = $scope.labels
|
||||
.map(val => val.value);
|
||||
var labelsToDisassociate = current
|
||||
.filter(val => labelsToAdd
|
||||
.indexOf(val) === -1)
|
||||
.map(val => ({id: val, disassociate: true}));
|
||||
var labelsToAssociate = labelsToAdd
|
||||
.filter(val => current
|
||||
.indexOf(val) === -1)
|
||||
.map(val => ({id: val, associate: true}));
|
||||
var pass = labelsToDisassociate
|
||||
.concat(labelsToAssociate);
|
||||
associationDefer.resolve(pass);
|
||||
});
|
||||
|
||||
Rest.setUrl(GetBasePath("organizations"));
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
orgDefer.resolve(data.results[0].id);
|
||||
});
|
||||
|
||||
orgDefer.promise.then(function(orgId) {
|
||||
var toPost = [];
|
||||
$scope.newLabels = $scope.newLabels
|
||||
.map(function(i, val) {
|
||||
val.organization = orgId;
|
||||
return val;
|
||||
});
|
||||
|
||||
orgDefer.promise.then(function(orgId) {
|
||||
var toPost = [];
|
||||
$scope.newLabels = $scope.newLabels
|
||||
.map(function(i, val) {
|
||||
val.organization = orgId;
|
||||
return val;
|
||||
$scope.newLabels.each(function(i, val) {
|
||||
toPost.push(val);
|
||||
});
|
||||
|
||||
associationDefer.promise.then(function(arr) {
|
||||
toPost = toPost
|
||||
.concat(arr);
|
||||
|
||||
Rest.setUrl($scope.workflow_job_template_obj.related.labels);
|
||||
|
||||
var defers = [];
|
||||
for (var i = 0; i < toPost.length; i++) {
|
||||
defers.push(Rest.post(toPost[i]));
|
||||
}
|
||||
$q.all(defers)
|
||||
.then(function() {
|
||||
$state.go('templates.editWorkflowJobTemplate', {id: id}, {reload: true});
|
||||
});
|
||||
|
||||
$scope.newLabels.each(function(i, val) {
|
||||
toPost.push(val);
|
||||
});
|
||||
|
||||
associationDefer.promise.then(function(arr) {
|
||||
toPost = toPost
|
||||
.concat(arr);
|
||||
|
||||
Rest.setUrl($scope.workflow_job_template_obj.related.labels);
|
||||
|
||||
var defers = [];
|
||||
for (var i = 0; i < toPost.length; i++) {
|
||||
defers.push(Rest.post(toPost[i]));
|
||||
}
|
||||
$q.all(defers)
|
||||
.then(function() {
|
||||
$state.go('templates.editWorkflowJobTemplate', {id: id}, {reload: true});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}, function(error){
|
||||
ProcessErrors($scope, error.data, error.status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to update workflow job template. PUT returned ' +
|
||||
'status: ' + error.status
|
||||
});
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
Wait('stop');
|
||||
|
||||
@ -29,6 +29,11 @@
|
||||
fill: @default-interface-txt;
|
||||
}
|
||||
|
||||
.WorkflowChart-startText {
|
||||
fill: @default-bg;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.node .rect {
|
||||
fill: @default-secondary-bg;
|
||||
}
|
||||
@ -97,3 +102,6 @@
|
||||
width: 90px;
|
||||
color: @default-interface-txt;
|
||||
}
|
||||
.WorkflowChart-activeNode {
|
||||
fill: @default-link;
|
||||
}
|
||||
|
||||
@ -84,6 +84,25 @@ export default [ '$state',
|
||||
}
|
||||
}
|
||||
|
||||
function rounded_rect(x, y, w, h, r, tl, tr, bl, br) {
|
||||
var retval;
|
||||
retval = "M" + (x + r) + "," + y;
|
||||
retval += "h" + (w - 2*r);
|
||||
if (tr) { retval += "a" + r + "," + r + " 0 0 1 " + r + "," + r; }
|
||||
else { retval += "h" + r; retval += "v" + r; }
|
||||
retval += "v" + (h - 2*r);
|
||||
if (br) { retval += "a" + r + "," + r + " 0 0 1 " + -r + "," + r; }
|
||||
else { retval += "v" + r; retval += "h" + -r; }
|
||||
retval += "h" + (2*r - w);
|
||||
if (bl) { retval += "a" + r + "," + r + " 0 0 1 " + -r + "," + -r; }
|
||||
else { retval += "h" + -r; retval += "v" + -r; }
|
||||
retval += "v" + (2*r - h);
|
||||
if (tl) { retval += "a" + r + "," + r + " 0 0 1 " + r + "," + -r; }
|
||||
else { retval += "v" + -r; retval += "h" + r; }
|
||||
retval += "z";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// This is the zoom function called by using the mousewheel/click and drag
|
||||
function naturalZoom() {
|
||||
let scale = d3.event.scale,
|
||||
@ -163,20 +182,13 @@ export default [ '$state',
|
||||
.attr("fill", "#5cb85c")
|
||||
.attr("class", "WorkflowChart-rootNode")
|
||||
.call(add_node);
|
||||
thisNode.append("path")
|
||||
.style("fill", "white")
|
||||
.attr("transform", function() { return "translate(" + 30 + "," + 30 + ")"; })
|
||||
.attr("d", d3.svg.symbol()
|
||||
.size(120)
|
||||
.type("cross")
|
||||
)
|
||||
.call(add_node);
|
||||
thisNode.append("text")
|
||||
.attr("x", 14)
|
||||
.attr("y", 0)
|
||||
.attr("x", 13)
|
||||
.attr("y", 30)
|
||||
.attr("dy", ".35em")
|
||||
.attr("class", "WorkflowChart-defaultText")
|
||||
.text(function () { return "START"; });
|
||||
.attr("class", "WorkflowChart-startText")
|
||||
.text(function () { return "START"; })
|
||||
.call(add_node);
|
||||
}
|
||||
else {
|
||||
thisNode.append("rect")
|
||||
@ -184,12 +196,32 @@ export default [ '$state',
|
||||
.attr("height", rectH)
|
||||
.attr("rx", 5)
|
||||
.attr("ry", 5)
|
||||
.attr('stroke', function(d) { return d.isActiveEdit ? "#337ab7" : "#D7D7D7"; })
|
||||
.attr('stroke-width', function(d){ return d.isActiveEdit ? "2px" : "1px"; })
|
||||
.attr('stroke', function(d) {
|
||||
if(d.edgeType) {
|
||||
if(d.edgeType === "failure") {
|
||||
return "#d9534f";
|
||||
}
|
||||
else if(d.edgeType === "success") {
|
||||
return "#5cb85c";
|
||||
}
|
||||
else if(d.edgeType === "always"){
|
||||
return "#337ab7";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return "#D7D7D7";
|
||||
}
|
||||
})
|
||||
.attr('stroke-width', "2px")
|
||||
.attr("class", function(d) {
|
||||
return d.placeholder ? "rect placeholder" : "rect";
|
||||
});
|
||||
|
||||
thisNode.append("path")
|
||||
.attr("d", rounded_rect(1, 0, 5, rectH, 5, 1, 0, 1, 0))
|
||||
.attr("class", "WorkflowChart-activeNode")
|
||||
.style("display", function(d) { return d.isActiveEdit ? null : "none"; });
|
||||
|
||||
thisNode.append("text")
|
||||
.attr("x", function(d){ return (scope.mode === 'details' && d.job && d.job.jobStatus) ? 20 : rectW / 2; })
|
||||
.attr("y", function(d){ return (scope.mode === 'details' && d.job && d.job.jobStatus) ? 10 : rectH / 2; })
|
||||
@ -517,8 +549,22 @@ export default [ '$state',
|
||||
.attr("transform", function(d) { return "translate(" + (d.target.y + d.source.y + rectW) / 2 + "," + (d.target.x + d.source.x + rectH) / 2 + ")"; });
|
||||
|
||||
t.selectAll(".rect")
|
||||
.attr('stroke', function(d) { return d.isActiveEdit ? "#337ab7" : "#D7D7D7"; })
|
||||
.attr('stroke-width', function(d){ return d.isActiveEdit ? "2px" : "1px"; })
|
||||
.attr('stroke', function(d) {
|
||||
if(d.edgeType) {
|
||||
if(d.edgeType === "failure") {
|
||||
return "#d9534f";
|
||||
}
|
||||
else if(d.edgeType === "success") {
|
||||
return "#5cb85c";
|
||||
}
|
||||
else if(d.edgeType === "always"){
|
||||
return "#337ab7";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return "#D7D7D7";
|
||||
}
|
||||
})
|
||||
.attr("class", function(d) {
|
||||
return d.placeholder ? "rect placeholder" : "rect";
|
||||
});
|
||||
@ -601,6 +647,9 @@ export default [ '$state',
|
||||
t.selectAll(".WorkflowChart-conflictText")
|
||||
.style("display", function(d) { return (d.edgeConflict && !d.placeholder) ? null : "none"; });
|
||||
|
||||
t.selectAll(".WorkflowChart-activeNode")
|
||||
.style("display", function(d) { return d.isActiveEdit ? null : "none"; });
|
||||
|
||||
}
|
||||
|
||||
function add_node() {
|
||||
|
||||
@ -154,7 +154,7 @@
|
||||
padding-left: 20px;
|
||||
}
|
||||
.WorkflowLegend-maker--right {
|
||||
flex: 0 0 182px;
|
||||
flex: 0 0 206px;
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
position: relative;
|
||||
@ -226,7 +226,7 @@
|
||||
}
|
||||
.WorkflowMaker-manualControls {
|
||||
position: absolute;
|
||||
left: -122px;
|
||||
left: -86px;
|
||||
height: 60px;
|
||||
width: 293px;
|
||||
background-color: @default-bg;
|
||||
|
||||
@ -35,6 +35,10 @@ export default ['$scope', 'WorkflowService', 'generateList', 'TemplateList', 'Pr
|
||||
showTypeOptions: false
|
||||
};
|
||||
|
||||
$scope.editRequests = [];
|
||||
$scope.associateRequests = [];
|
||||
$scope.disassociateRequests = [];
|
||||
|
||||
function init() {
|
||||
$scope.treeDataMaster = angular.copy($scope.treeData.data);
|
||||
$scope.showManualControls = false;
|
||||
@ -55,6 +59,160 @@ export default ['$scope', 'WorkflowService', 'generateList', 'TemplateList', 'Pr
|
||||
$scope.workflowMakerFormConfig.activeTab = "jobs";
|
||||
}
|
||||
|
||||
function recursiveNodeUpdates(params, completionCallback) {
|
||||
// params.parentId
|
||||
// params.node
|
||||
|
||||
let generatePostUrl = function(){
|
||||
|
||||
let base = (params.parentId) ? GetBasePath('workflow_job_template_nodes') + params.parentId : $scope.treeData.workflow_job_template_obj.related.workflow_nodes;
|
||||
|
||||
if(params.parentId) {
|
||||
if(params.node.edgeType === 'success') {
|
||||
base += "/success_nodes";
|
||||
}
|
||||
else if(params.node.edgeType === 'failure') {
|
||||
base += "/failure_nodes";
|
||||
}
|
||||
else if(params.node.edgeType === 'always') {
|
||||
base += "/always_nodes";
|
||||
}
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
};
|
||||
|
||||
let buildSendableNodeData = function() {
|
||||
// Create the node
|
||||
let sendableNodeData = {
|
||||
unified_job_template: params.node.unifiedJobTemplate.id
|
||||
};
|
||||
|
||||
// Check to see if the user has provided any prompt values that are different
|
||||
// from the defaults in the job template
|
||||
|
||||
if(params.node.unifiedJobTemplate.type === "job_template" && params.node.promptValues) {
|
||||
if(params.node.unifiedJobTemplate.ask_credential_on_launch) {
|
||||
sendableNodeData.credential = !params.node.promptValues.credential || params.node.unifiedJobTemplate.summary_fields.credential.id !== params.node.promptValues.credential.id ? params.node.promptValues.credential.id : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_inventory_on_launch) {
|
||||
sendableNodeData.inventory = !params.node.promptValues.inventory || params.node.unifiedJobTemplate.summary_fields.inventory.id !== params.node.promptValues.inventory.id ? params.node.promptValues.inventory.id : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_limit_on_launch) {
|
||||
sendableNodeData.limit = !params.node.promptValues.limit || params.node.unifiedJobTemplate.limit !== params.node.promptValues.limit ? params.node.promptValues.limit : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_job_type_on_launch) {
|
||||
sendableNodeData.job_type = !params.node.promptValues.job_type || params.node.unifiedJobTemplate.job_type !== params.node.promptValues.job_type ? params.node.promptValues.job_type : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_tags_on_launch) {
|
||||
sendableNodeData.job_tags = !params.node.promptValues.job_tags || params.node.unifiedJobTemplate.job_tags !== params.node.promptValues.job_tags ? params.node.promptValues.job_tags : null;
|
||||
}
|
||||
if(params.node.unifiedJobTemplate.ask_skip_tags_on_launch) {
|
||||
sendableNodeData.skip_tags = !params.node.promptValues.skip_tags || params.node.unifiedJobTemplate.skip_tags !== params.node.promptValues.skip_tags ? params.node.promptValues.skip_tags : null;
|
||||
}
|
||||
}
|
||||
|
||||
return sendableNodeData;
|
||||
};
|
||||
|
||||
let continueRecursing = function(parentId) {
|
||||
$scope.totalIteratedNodes++;
|
||||
|
||||
if($scope.totalIteratedNodes === $scope.treeData.data.totalNodes) {
|
||||
// We're done recursing, lets move on
|
||||
completionCallback();
|
||||
}
|
||||
else {
|
||||
if(params.node.children && params.node.children.length > 0) {
|
||||
_.forEach(params.node.children, function(child) {
|
||||
if(child.edgeType === "success") {
|
||||
recursiveNodeUpdates({
|
||||
parentId: parentId,
|
||||
node: child
|
||||
}, completionCallback);
|
||||
}
|
||||
else if(child.edgeType === "failure") {
|
||||
recursiveNodeUpdates({
|
||||
parentId: parentId,
|
||||
node: child
|
||||
}, completionCallback);
|
||||
}
|
||||
else if(child.edgeType === "always") {
|
||||
recursiveNodeUpdates({
|
||||
parentId: parentId,
|
||||
node: child
|
||||
}, completionCallback);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(params.node.isNew) {
|
||||
|
||||
TemplatesService.addWorkflowNode({
|
||||
url: generatePostUrl(),
|
||||
data: buildSendableNodeData()
|
||||
})
|
||||
.then(function(data) {
|
||||
continueRecursing(data.data.id);
|
||||
}, function(error) {
|
||||
ProcessErrors($scope, error.data, error.status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add workflow node. ' +
|
||||
'POST returned status: ' +
|
||||
error.status
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
if(params.node.edited || !params.node.originalParentId || (params.node.originalParentId && params.parentId !== params.node.originalParentId)) {
|
||||
|
||||
if(params.node.edited) {
|
||||
|
||||
$scope.editRequests.push({
|
||||
id: params.node.nodeId,
|
||||
data: buildSendableNodeData()
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep
|
||||
|
||||
$scope.disassociateRequests.push({
|
||||
parentId: params.node.originalParentId,
|
||||
nodeId: params.node.nodeId,
|
||||
edge: params.node.originalEdge
|
||||
});
|
||||
|
||||
// Can only associate if we have a parent.
|
||||
// If we don't have a parent then this is a root node
|
||||
// and the act of disassociating will make it a root node
|
||||
if(params.parentId) {
|
||||
$scope.associateRequests.push({
|
||||
parentId: params.parentId,
|
||||
nodeId: params.node.nodeId,
|
||||
edge: params.node.edgeType
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
else if(!params.node.originalParentId && params.parentId) {
|
||||
// This used to be a root node but is now not a root node
|
||||
$scope.associateRequests.push({
|
||||
parentId: params.parentId,
|
||||
nodeId: params.node.nodeId,
|
||||
edge: params.node.edgeType
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
continueRecursing(params.node.nodeId);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.lookUpInventory = function(){
|
||||
$state.go('.inventory');
|
||||
};
|
||||
@ -70,7 +228,66 @@ export default ['$scope', 'WorkflowService', 'generateList', 'TemplateList', 'Pr
|
||||
};
|
||||
|
||||
$scope.saveWorkflowMaker = function() {
|
||||
$scope.closeDialog();
|
||||
|
||||
$scope.totalIteratedNodes = 0;
|
||||
|
||||
if($scope.treeData && $scope.treeData.data && $scope.treeData.data.children && $scope.treeData.data.children.length > 0) {
|
||||
let completionCallback = function() {
|
||||
|
||||
let disassociatePromises = $scope.disassociateRequests.map(function(request) {
|
||||
return TemplatesService.disassociateWorkflowNode({
|
||||
parentId: request.parentId,
|
||||
nodeId: request.nodeId,
|
||||
edge: request.edge
|
||||
});
|
||||
});
|
||||
|
||||
let editNodePromises = $scope.editRequests.map(function(request) {
|
||||
return TemplatesService.editWorkflowNode({
|
||||
id: request.id,
|
||||
data: request.data
|
||||
});
|
||||
});
|
||||
|
||||
$q.all(disassociatePromises.concat(editNodePromises))
|
||||
.then(function() {
|
||||
|
||||
let associatePromises = $scope.associateRequests.map(function(request) {
|
||||
return TemplatesService.associateWorkflowNode({
|
||||
parentId: request.parentId,
|
||||
nodeId: request.nodeId,
|
||||
edge: request.edge
|
||||
});
|
||||
});
|
||||
|
||||
let deletePromises = $scope.treeData.data.deletedNodes.map(function(nodeId) {
|
||||
return TemplatesService.deleteWorkflowJobTemplateNode(nodeId);
|
||||
});
|
||||
|
||||
$q.all(associatePromises.concat(deletePromises))
|
||||
.then(function() {
|
||||
$scope.closeDialog();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_.forEach($scope.treeData.data.children, function(child) {
|
||||
recursiveNodeUpdates({
|
||||
node: child
|
||||
}, completionCallback);
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
||||
let deletePromises = $scope.treeData.data.deletedNodes.map(function(nodeId) {
|
||||
return TemplatesService.deleteWorkflowJobTemplateNode(nodeId);
|
||||
});
|
||||
|
||||
$q.all(deletePromises)
|
||||
.then(function() {
|
||||
$scope.closeDialog();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* ADD NODE FUNCTIONS */
|
||||
@ -575,7 +792,7 @@ export default ['$scope', 'WorkflowService', 'generateList', 'TemplateList', 'Pr
|
||||
edgeFlags: $scope.edgeFlags
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$scope.toggleManualControls = function() {
|
||||
$scope.showManualControls = !$scope.showManualControls;
|
||||
};
|
||||
|
||||
@ -47,10 +47,10 @@ describe('Controller: WorkflowMaker', () => {
|
||||
|
||||
}));
|
||||
|
||||
describe('scope.saveWorkflowMaker()', () => {
|
||||
describe('scope.closeWorkflowMaker()', () => {
|
||||
|
||||
it('should close the dialog', ()=>{
|
||||
scope.saveWorkflowMaker();
|
||||
scope.closeWorkflowMaker();
|
||||
expect(scope.closeDialog).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user