Merge pull request #4014 from mabashian/3968-workflow-graph-on-details-v2

Add workflow graph to details view and re-org job-templates directory
This commit is contained in:
Michael Abashian
2016-11-15 14:13:30 -05:00
committed by GitHub
63 changed files with 556 additions and 513 deletions

View File

@@ -67,7 +67,7 @@ import moment from './shared/moment/main';
import login from './login/main'; import login from './login/main';
import activityStream from './activity-stream/main'; import activityStream from './activity-stream/main';
import standardOut from './standard-out/main'; import standardOut from './standard-out/main';
import JobTemplates from './job-templates/main'; import Templates from './templates/main';
import credentials from './credentials/main'; import credentials from './credentials/main';
import { ProjectsList, ProjectsAdd, ProjectsEdit } from './controllers/Projects'; import { ProjectsList, ProjectsAdd, ProjectsEdit } from './controllers/Projects';
import { UsersList, UsersAdd, UsersEdit } from './controllers/Users'; import { UsersList, UsersAdd, UsersEdit } from './controllers/Users';
@@ -126,7 +126,7 @@ var tower = angular.module('Tower', [
jobSubmission.name, jobSubmission.name,
notifications.name, notifications.name,
standardOut.name, standardOut.name,
JobTemplates.name, Templates.name,
portalMode.name, portalMode.name,
config.name, config.name,
credentials.name, credentials.name,
@@ -135,7 +135,7 @@ var tower = angular.module('Tower', [
'OrganizationFormDefinition', 'OrganizationFormDefinition',
'UserFormDefinition', 'UserFormDefinition',
'OrganizationListDefinition', 'OrganizationListDefinition',
'jobTemplates', 'templates',
'UserListDefinition', 'UserListDefinition',
'UserHelper', 'UserHelper',
'PromptDialog', 'PromptDialog',
@@ -158,7 +158,7 @@ var tower = angular.module('Tower', [
'TeamHelper', 'TeamHelper',
'CredentialsListDefinition', 'CredentialsListDefinition',
'CredentialFormDefinition', 'CredentialFormDefinition',
'JobTemplatesListDefinition', 'TemplatesListDefinition',
'PortalJobTemplatesListDefinition', 'PortalJobTemplatesListDefinition',
'JobTemplateFormDefinition', 'JobTemplateFormDefinition',
'JobTemplatesHelper', 'JobTemplatesHelper',

View File

@@ -14,7 +14,7 @@ function InventoriesEdit($scope, $rootScope, $compile, $location,
$log, $stateParams, InventoryForm, Rest, Alert, ProcessErrors, $log, $stateParams, InventoryForm, Rest, Alert, ProcessErrors,
ClearScope, GetBasePath, ParseTypeChange, Wait, ToJSON, ClearScope, GetBasePath, ParseTypeChange, Wait, ToJSON,
ParseVariableString, Prompt, InitiatePlaybookRun, ParseVariableString, Prompt, InitiatePlaybookRun,
JobTemplateService, $state, $filter) { TemplatesService, $state, $filter) {
// Inject dynamic view // Inject dynamic view
var defaultUrl = GetBasePath('inventory'), var defaultUrl = GetBasePath('inventory'),
@@ -146,7 +146,7 @@ function InventoriesEdit($scope, $rootScope, $compile, $location,
action = function () { action = function () {
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
Wait('start'); Wait('start');
JobTemplateService.deleteJobTemplate(id) TemplatesService.deleteJobTemplate(id)
.success(function () { .success(function () {
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
// @issue: OLD SEARCH // @issue: OLD SEARCH
@@ -174,5 +174,5 @@ export default ['$scope', '$rootScope', '$compile', '$location',
'$log', '$stateParams', 'InventoryForm', 'Rest', 'Alert', '$log', '$stateParams', 'InventoryForm', 'Rest', 'Alert',
'ProcessErrors', 'ClearScope', 'GetBasePath', 'ParseTypeChange', 'Wait', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'ParseTypeChange', 'Wait',
'ToJSON', 'ParseVariableString', 'Prompt', 'InitiatePlaybookRun', 'ToJSON', 'ParseVariableString', 'Prompt', 'InitiatePlaybookRun',
'JobTemplateService', '$state', '$filter', InventoriesEdit, 'TemplatesService', '$state', '$filter', InventoriesEdit,
]; ];

View File

@@ -1,127 +0,0 @@
export default ['CreateDialog', 'Wait', '$q', '$state', function(CreateDialog, Wait, $q, $state){
return {
closeDialog: function() {
$('#workflow-modal-dialog').dialog('destroy');
$state.go('^');
},
searchTree: function(params) {
// params.element
// params.matchingId
if(params.element.id === 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
});
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";
}
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
};
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) {
placeholder.children.push(angular.copy(child));
parentNode.children[index] = placeholder;
placeholderRef = 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.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.placeholder && child.edgeType) {
siblingConnectionTypes[child.edgeType] = true;
}
});
}
return Object.keys(siblingConnectionTypes);
}
};
}];

View File

@@ -16,7 +16,6 @@ import InventoryHosts from "./lists/InventoryHosts";
import InventorySources from "./lists/InventorySources"; import InventorySources from "./lists/InventorySources";
import JobEvents from "./lists/JobEvents"; import JobEvents from "./lists/JobEvents";
import JobHosts from "./lists/JobHosts"; import JobHosts from "./lists/JobHosts";
import JobTemplates from "./lists/JobTemplates";
import Jobs from "./lists/Jobs"; import Jobs from "./lists/Jobs";
import Organizations from "./lists/Organizations"; import Organizations from "./lists/Organizations";
import PortalJobTemplates from "./lists/PortalJobTemplates"; import PortalJobTemplates from "./lists/PortalJobTemplates";
@@ -27,6 +26,7 @@ import ScheduledJobs from "./lists/ScheduledJobs";
import Schedules from "./lists/Schedules"; import Schedules from "./lists/Schedules";
import Streams from "./lists/Streams"; import Streams from "./lists/Streams";
import Teams from "./lists/Teams"; import Teams from "./lists/Teams";
import Templates from "./lists/Templates";
import Users from "./lists/Users"; import Users from "./lists/Users";
export export
@@ -42,7 +42,6 @@ export
InventorySources, InventorySources,
JobEvents, JobEvents,
JobHosts, JobHosts,
JobTemplates,
Jobs, Jobs,
Organizations, Organizations,
PortalJobTemplates, PortalJobTemplates,
@@ -53,5 +52,6 @@ export
Schedules, Schedules,
Streams, Streams,
Teams, Teams,
Templates,
Users Users
}; };

View File

@@ -6,8 +6,8 @@
export default export default
angular.module('JobTemplatesListDefinition', []) angular.module('TemplatesListDefinition', [])
.factory('JobTemplateList', ['i18n', function(i18n) { .factory('TemplateList', ['i18n', function(i18n) {
return { return {
name: 'templates', name: 'templates',

View File

@@ -288,13 +288,15 @@ export default [{
features: ['FeaturesService', function(FeaturesService) { features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get(); return FeaturesService.get();
}], }],
OrgJobTemplateList: ['JobTemplateList', 'GetBasePath', '$stateParams', function(JobTemplateList) { OrgJobTemplateList: ['TemplateList', 'GetBasePath', '$stateParams', function(TemplateList) {
let list = _.cloneDeep(JobTemplateList); let list = _.cloneDeep(TemplateList);
delete list.actions; delete list.actions;
// @issue Why is the delete action unavailable in this view? // @issue Why is the delete action unavailable in this view?
delete list.fieldActions.delete; delete list.fieldActions.delete;
list.emptyListText = "This list is populated by job templates added from the&nbsp;<a ui-sref='jobTemplates.add'>Job Templates</a>&nbsp;section"; list.emptyListText = "This list is populated by job templates added from the&nbsp;<a ui-sref='jobTemplates.add'>Job Templates</a>&nbsp;section";
list.searchSize = "col-lg-12 col-md-12 col-sm-12 col-xs-12"; list.searchSize = "col-lg-12 col-md-12 col-sm-12 col-xs-12";
list.iterator = 'job_template';
list.name = 'job_templates';
return list; return list;
}], }],
OrgJobTemplateDataset: ['OrgJobTemplateList', 'QuerySet', '$stateParams', 'GetBasePath', OrgJobTemplateDataset: ['OrgJobTemplateList', 'QuerySet', '$stateParams', 'GetBasePath',

View File

@@ -9,7 +9,7 @@ import route from './job-templates-copy.route';
import service from './job-templates-copy.service'; import service from './job-templates-copy.service';
export default export default
angular.module('jobTemplates.copy', []) angular.module('templates.copy', [])
.service('jobTemplateCopyService', service) .service('jobTemplateCopyService', service)
.controller('jobTemplateCopyController', controller) .controller('jobTemplateCopyController', controller)
.run(['$stateExtender', function($stateExtender) { .run(['$stateExtender', function($stateExtender) {

View File

@@ -12,7 +12,7 @@ export default
return { return {
restrict: 'E', restrict: 'E',
scope: false, scope: false,
templateUrl: templateUrl('job-templates/labels/labelsList'), templateUrl: templateUrl('templates/labels/labelsList'),
link: function(scope, element, attrs) { link: function(scope, element, attrs) {
scope.showDelete = attrs.showDelete === 'true'; scope.showDelete = attrs.showDelete === 'true';
scope.seeMoreInactive = true; scope.seeMoreInactive = true;

View File

@@ -4,8 +4,8 @@
* All Rights Reserved * All Rights Reserved
*************************************************/ *************************************************/
import controller from './job-templates-list.controller'; import controller from './templates-list.controller';
export default export default
angular.module('jobTemplatesList', []) angular.module('templatesList', [])
.controller('JobTemplatesListController', controller); .controller('TemplatesListController', controller);

View File

@@ -5,18 +5,18 @@
*************************************************/ *************************************************/
export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', 'Alert', export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', 'Alert',
'JobTemplateList', 'Prompt', 'ClearScope', 'ProcessErrors', 'GetBasePath', 'TemplateList', 'Prompt', 'ClearScope', 'ProcessErrors', 'GetBasePath',
'InitiatePlaybookRun', 'Wait', '$state', '$filter', 'Dataset', 'rbacUiControlService', 'JobTemplateService', 'InitiatePlaybookRun', 'Wait', '$state', '$filter', 'Dataset', 'rbacUiControlService', 'TemplatesService',
'QuerySet', 'QuerySet',
function( function(
$scope, $rootScope, $location, $stateParams, Rest, Alert, $scope, $rootScope, $location, $stateParams, Rest, Alert,
JobTemplateList, Prompt, ClearScope, ProcessErrors, GetBasePath, TemplateList, Prompt, ClearScope, ProcessErrors, GetBasePath,
InitiatePlaybookRun, Wait, $state, $filter, Dataset, rbacUiControlService, JobTemplateService, InitiatePlaybookRun, Wait, $state, $filter, Dataset, rbacUiControlService, TemplatesService,
qs qs
) { ) {
ClearScope(); ClearScope();
var list = JobTemplateList; var list = TemplateList;
init(); init();
@@ -95,7 +95,7 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', 'Al
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
Wait('start'); Wait('start');
if(template.type && (template.type === 'Workflow Job Template' || template.type === 'workflow_job_template')) { if(template.type && (template.type === 'Workflow Job Template' || template.type === 'workflow_job_template')) {
JobTemplateService.deleteWorkflowJobTemplate(template.id) TemplatesService.deleteWorkflowJobTemplate(template.id)
.then(function () { .then(function () {
handleSuccessfulDelete(); handleSuccessfulDelete();
}, function (data) { }, function (data) {
@@ -105,7 +105,7 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', 'Al
}); });
} }
else if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) { else if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) {
JobTemplateService.deleteJobTemplate(template.id) TemplatesService.deleteJobTemplate(template.id)
.then(function () { .then(function () {
handleSuccessfulDelete(); handleSuccessfulDelete();
}, function (data) { }, function (data) {

View File

@@ -27,10 +27,10 @@ export default {
searchPrefix: 'template', searchPrefix: 'template',
views: { views: {
'@': { '@': {
controller: 'JobTemplatesListController', controller: 'TemplatesListController',
templateProvider: function(JobTemplateList, generateList) { templateProvider: function(TemplateList, generateList) {
let html = generateList.build({ let html = generateList.build({
list: JobTemplateList, list: TemplateList,
mode: 'edit' mode: 'edit'
}); });
html = generateList.wrapPanel(html); html = generateList.wrapPanel(html);
@@ -39,7 +39,7 @@ export default {
} }
}, },
resolve: { resolve: {
Dataset: ['JobTemplateList', 'QuerySet', '$stateParams', 'GetBasePath', Dataset: ['TemplateList', 'QuerySet', '$stateParams', 'GetBasePath',
function(list, qs, $stateParams, GetBasePath) { function(list, qs, $stateParams, GetBasePath) {
let path = GetBasePath(list.basePath) || GetBasePath(list.name); let path = GetBasePath(list.basePath) || GetBasePath(list.name);
return qs.search(path, $stateParams[`${list.iterator}_search`]); return qs.search(path, $stateParams[`${list.iterator}_search`]);

View File

@@ -4,26 +4,27 @@
* All Rights Reserved * All Rights Reserved
*************************************************/ *************************************************/
import jobTemplateService from './job-template.service'; import templatesService from './templates.service';
import surveyMaker from './survey-maker/main'; import surveyMaker from './survey-maker/main';
import jobTemplatesList from './list/main'; import templatesList from './list/main';
import jobTemplatesAdd from './add-job-template/main'; import jobTemplatesAdd from './job_templates/add-job-template/main';
import jobTemplatesEdit from './edit-job-template/main'; import jobTemplatesEdit from './job_templates/edit-job-template/main';
import jobTemplatesCopy from './copy/main'; import jobTemplatesCopy from './job_templates/copy-job-template/main';
import workflowAdd from './add-workflow/main'; import workflowAdd from './workflows/add-workflow/main';
import workflowEdit from './edit-workflow/main'; import workflowEdit from './workflows/edit-workflow/main';
import labels from './labels/main'; import labels from './labels/main';
import workflowChart from './workflow-chart/main'; import workflowChart from './workflows/workflow-chart/main';
import workflowMaker from './workflow-maker/main'; import workflowMaker from './workflows/workflow-maker/main';
import jobTemplatesListRoute from './list/job-templates-list.route'; import templatesListRoute from './list/templates-list.route';
import workflowService from './workflows/workflow.service';
export default export default
angular.module('jobTemplates', [surveyMaker.name, jobTemplatesList.name, jobTemplatesAdd.name, angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesAdd.name,
jobTemplatesEdit.name, jobTemplatesCopy.name, labels.name, workflowAdd.name, workflowEdit.name, jobTemplatesEdit.name, jobTemplatesCopy.name, labels.name, workflowAdd.name, workflowEdit.name,
workflowChart.name, workflowMaker.name workflowChart.name, workflowMaker.name
]) ])
.service('JobTemplateService', jobTemplateService) .service('TemplatesService', templatesService)
.service('WorkflowService', workflowService)
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider', .config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) { function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow, let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow,
@@ -332,9 +333,9 @@ angular.module('jobTemplates', [surveyMaker.name, jobTemplatesList.name, jobTemp
return qs.search(path, $stateParams[`${list.iterator}_search`]); return qs.search(path, $stateParams[`${list.iterator}_search`]);
} }
], ],
WorkflowMakerJobTemplateList: ['JobTemplateList', WorkflowMakerJobTemplateList: ['TemplateList',
(JobTemplateList) => { (TemplateList) => {
let list = _.cloneDeep(JobTemplateList); let list = _.cloneDeep(TemplateList);
delete list.fields.type; delete list.fields.type;
delete list.fields.description; delete list.fields.description;
delete list.fields.smart_status; delete list.fields.smart_status;
@@ -472,7 +473,7 @@ angular.module('jobTemplates', [surveyMaker.name, jobTemplatesList.name, jobTemp
states: _.reduce(generated, (result, definition) => { states: _.reduce(generated, (result, definition) => {
return result.concat(definition.states); return result.concat(definition.states);
}, [ }, [
stateExtender.buildDefinition(jobTemplatesListRoute), stateExtender.buildDefinition(templatesListRoute),
stateExtender.buildDefinition(workflowMaker), stateExtender.buildDefinition(workflowMaker),
stateExtender.buildDefinition(inventoryLookup), stateExtender.buildDefinition(inventoryLookup),
stateExtender.buildDefinition(credentialLookup) stateExtender.buildDefinition(credentialLookup)

View File

@@ -5,7 +5,7 @@ import render from './render/main';
import shared from './shared/main'; import shared from './shared/main';
export default export default
angular.module('jobTemplates.surveyMaker', angular.module('templates.surveyMaker',
[ listGenerator.name, [ listGenerator.name,
questions.name, questions.name,
surveys.name, surveys.name,

View File

@@ -6,11 +6,11 @@
export default export default
[ '$scope', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors', 'ClearScope', [ '$scope', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors', 'ClearScope',
'Wait', '$state', 'CreateSelect2', 'JobTemplateService', 'ToJSON', 'Wait', '$state', 'CreateSelect2', 'TemplatesService', 'ToJSON',
'ParseTypeChange', 'OrganizationList', '$q', 'Rest', 'GetBasePath', 'ParseTypeChange', 'OrganizationList', '$q', 'Rest', 'GetBasePath',
function( function(
$scope, WorkflowForm, GenerateForm, Alert, ProcessErrors, ClearScope, $scope, WorkflowForm, GenerateForm, Alert, ProcessErrors, ClearScope,
Wait, $state, CreateSelect2, JobTemplateService, ToJSON, Wait, $state, CreateSelect2, TemplatesService, ToJSON,
ParseTypeChange, OrganizationList, $q, Rest, GetBasePath ParseTypeChange, OrganizationList, $q, Rest, GetBasePath
) { ) {
@@ -45,7 +45,7 @@
}); });
// Go out and grab the possible labels // Go out and grab the possible labels
JobTemplateService.getLabelOptions() TemplatesService.getLabelOptions()
.then(function(data){ .then(function(data){
$scope.labelOptions = data; $scope.labelOptions = data;
// select2-ify the labels input // select2-ify the labels input
@@ -98,7 +98,7 @@
.filter("[data-label-is-present=true]") .filter("[data-label-is-present=true]")
.map((i, val) => ({name: $(val).text()})); .map((i, val) => ({name: $(val).text()}));
JobTemplateService.createWorkflowJobTemplate(data) TemplatesService.createWorkflowJobTemplate(data)
.then(function(data) { .then(function(data) {
let orgDefer = $q.defer(); let orgDefer = $q.defer();

View File

@@ -8,13 +8,13 @@
[ '$scope', '$stateParams', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors', [ '$scope', '$stateParams', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors',
'ClearScope', 'GetBasePath', '$q', 'ParseTypeChange', 'Wait', 'Empty', 'ClearScope', 'GetBasePath', '$q', 'ParseTypeChange', 'Wait', 'Empty',
'ToJSON', 'initSurvey', '$state', 'CreateSelect2', 'ParseVariableString', 'ToJSON', 'initSurvey', '$state', 'CreateSelect2', 'ParseVariableString',
'JobTemplateService', 'OrganizationList', 'Rest', 'TemplatesService', 'OrganizationList', 'Rest', 'WorkflowService',
function( function(
$scope, $stateParams, WorkflowForm, GenerateForm, Alert, ProcessErrors, $scope, $stateParams, WorkflowForm, GenerateForm, Alert, ProcessErrors,
ClearScope, GetBasePath, $q, ParseTypeChange, Wait, Empty, ClearScope, GetBasePath, $q, ParseTypeChange, Wait, Empty,
ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString, ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
JobTemplateService, OrganizationList, Rest TemplatesService, OrganizationList, Rest, WorkflowService
) {window.state = $state; ) {
ClearScope(); ClearScope();
@@ -40,90 +40,6 @@
$scope.associateRequests = []; $scope.associateRequests = [];
$scope.disassociateRequests = []; $scope.disassociateRequests = [];
$scope.workflowTree = {
data: {
id: 1,
canDelete: false,
canEdit: false,
canAddTo: true,
isStartNode: true,
unifiedJobTemplate: {
name: "Workflow Launch"
},
children: [],
deletedNodes: [],
totalNodes: 0
},
nextIndex: 2
};
function buildBranch(params) {
// params.nodeId
// params.parentId
// params.edgeType
// params.nodesObj
// params.isRoot
let treeNode = {
children: [],
c: "#D7D7D7",
id: $scope.workflowTree.nextIndex,
nodeId: params.nodeId,
canDelete: true,
canEdit: true,
canAddTo: true,
placeholder: false,
edgeType: params.edgeType,
unifiedJobTemplate: _.clone(params.nodesObj[params.nodeId].summary_fields.unified_job_template),
isNew: false,
edited: false,
originalEdge: params.edgeType,
originalNodeObj: _.clone(params.nodesObj[params.nodeId]),
promptValues: {},
isRoot: params.isRoot ? params.isRoot : false
};
$scope.workflowTree.data.totalNodes++;
$scope.workflowTree.nextIndex++;
if(params.parentId) {
treeNode.originalParentId = params.parentId;
}
// Loop across the success nodes and add them recursively
_.forEach(params.nodesObj[params.nodeId].success_nodes, function(successNodeId) {
treeNode.children.push(buildBranch({
nodeId: successNodeId,
parentId: params.nodeId,
edgeType: "success",
nodesObj: params.nodesObj
}));
});
// failure nodes
_.forEach(params.nodesObj[params.nodeId].failure_nodes, function(failureNodesId) {
treeNode.children.push(buildBranch({
nodeId: failureNodesId,
parentId: params.nodeId,
edgeType: "failure",
nodesObj: params.nodesObj
}));
});
// always nodes
_.forEach(params.nodesObj[params.nodeId].always_nodes, function(alwaysNodesId) {
treeNode.children.push(buildBranch({
nodeId: alwaysNodesId,
parentId: params.nodeId,
edgeType: "always",
nodesObj: params.nodesObj
}));
});
return treeNode;
}
function init() { function init() {
// Select2-ify the lables input // Select2-ify the lables input
@@ -192,43 +108,11 @@
}); });
// Get the workflow nodes // Get the workflow nodes
JobTemplateService.getWorkflowJobTemplateNodes(id) TemplatesService.getWorkflowJobTemplateNodes(id)
.then(function(data){ .then(function(data){
let nodesArray = data.data.results; $scope.workflowTree = WorkflowService.buildTree({
let nodesObj = {}; workflowNodes: data.data.results
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 = buildBranch({
nodeId: rootNodeId,
edgeType: "always",
nodesObj: nodesObj,
isRoot: true
});
$scope.workflowTree.data.children.push(branch);
}); });
// TODO: I think that the workflow chart directive (and eventually d3) is meddling with // TODO: I think that the workflow chart directive (and eventually d3) is meddling with
@@ -251,7 +135,7 @@
}); });
// Go out and GET the workflow job temlate data needed to populate the form // Go out and GET the workflow job temlate data needed to populate the form
JobTemplateService.getWorkflowJobTemplate(id) TemplatesService.getWorkflowJobTemplate(id)
.then(function(data){ .then(function(data){
let workflowJobTemplateData = data.data; let workflowJobTemplateData = data.data;
$scope.workflow_job_template_obj = workflowJobTemplateData; $scope.workflow_job_template_obj = workflowJobTemplateData;
@@ -392,7 +276,7 @@
if(params.node.isNew) { if(params.node.isNew) {
JobTemplateService.addWorkflowNode({ TemplatesService.addWorkflowNode({
url: generatePostUrl(), url: generatePostUrl(),
data: buildSendableNodeData() data: buildSendableNodeData()
}) })
@@ -500,7 +384,7 @@
// these promise arrays to play nicely. I tried to just append // these promise arrays to play nicely. I tried to just append
// a single promise to deletePromises but it just wasn't working // a single promise to deletePromises but it just wasn't working
let editWorkflowJobTemplate = [id].map(function(id) { let editWorkflowJobTemplate = [id].map(function(id) {
return JobTemplateService.updateWorkflowJobTemplate({ return TemplatesService.updateWorkflowJobTemplate({
id: id, id: id,
data: data data: data
}); });
@@ -510,7 +394,7 @@
let completionCallback = function() { let completionCallback = function() {
let disassociatePromises = $scope.disassociateRequests.map(function(request) { let disassociatePromises = $scope.disassociateRequests.map(function(request) {
return JobTemplateService.disassociateWorkflowNode({ return TemplatesService.disassociateWorkflowNode({
parentId: request.parentId, parentId: request.parentId,
nodeId: request.nodeId, nodeId: request.nodeId,
edge: request.edge edge: request.edge
@@ -518,7 +402,7 @@
}); });
let editNodePromises = $scope.editRequests.map(function(request) { let editNodePromises = $scope.editRequests.map(function(request) {
return JobTemplateService.editWorkflowNode({ return TemplatesService.editWorkflowNode({
id: request.id, id: request.id,
data: request.data data: request.data
}); });
@@ -528,7 +412,7 @@
.then(function() { .then(function() {
let associatePromises = $scope.associateRequests.map(function(request) { let associatePromises = $scope.associateRequests.map(function(request) {
return JobTemplateService.associateWorkflowNode({ return TemplatesService.associateWorkflowNode({
parentId: request.parentId, parentId: request.parentId,
nodeId: request.nodeId, nodeId: request.nodeId,
edge: request.edge edge: request.edge
@@ -536,7 +420,7 @@
}); });
let deletePromises = $scope.workflowTree.data.deletedNodes.map(function(nodeId) { let deletePromises = $scope.workflowTree.data.deletedNodes.map(function(nodeId) {
return JobTemplateService.deleteWorkflowJobTemplateNode(nodeId); return TemplatesService.deleteWorkflowJobTemplateNode(nodeId);
}); });
$q.all(associatePromises.concat(deletePromises)) $q.all(associatePromises.concat(deletePromises))
@@ -634,7 +518,7 @@
else { else {
let deletePromises = $scope.workflowTree.data.deletedNodes.map(function(nodeId) { let deletePromises = $scope.workflowTree.data.deletedNodes.map(function(nodeId) {
return JobTemplateService.deleteWorkflowJobTemplateNode(nodeId); return TemplatesService.deleteWorkflowJobTemplateNode(nodeId);
}); });
$q.all(deletePromises.concat(editWorkflowJobTemplate)) $q.all(deletePromises.concat(editWorkflowJobTemplate))

View File

@@ -7,5 +7,5 @@
import workflowChart from './workflow-chart.directive'; import workflowChart from './workflow-chart.directive';
export default export default
angular.module('jobTemplatesWorkflowChart', []) angular.module('workflowChart', [])
.directive('workflowChart', workflowChart); .directive('workflowChart', workflowChart);

View File

@@ -1,10 +1,8 @@
import helper from './workflow-help.service';
import workflowMaker from './workflow-maker.directive'; import workflowMaker from './workflow-maker.directive';
import WorkflowMakerController from './workflow-maker.controller'; import WorkflowMakerController from './workflow-maker.controller';
export default export default
angular.module('jobTemplates.workflowMaker', []) angular.module('templates.workflowMaker', [])
.service('WorkflowHelpService', helper)
// In order to test this controller I had to expose it at the module level // In order to test this controller I had to expose it at the module level
// like so. Is this correct? Is there a better pattern for doing this? // like so. Is this correct? Is there a better pattern for doing this?
.controller('WorkflowMakerController', WorkflowMakerController) .controller('WorkflowMakerController', WorkflowMakerController)

View File

@@ -4,12 +4,12 @@
* All Rights Reserved * All Rights Reserved
*************************************************/ *************************************************/
export default ['$scope', 'WorkflowHelpService', 'generateList', 'JobTemplateList', 'ProjectList', export default ['$scope', 'WorkflowService', 'generateList', 'TemplateList', 'ProjectList',
'GetBasePath', 'Wait', 'JobTemplateService', '$state', 'GetBasePath', 'Wait', 'TemplatesService', '$state',
'ProcessErrors', 'InventorySourcesList', 'CreateSelect2', 'WorkflowMakerForm', 'ProcessErrors', 'InventorySourcesList', 'CreateSelect2', 'WorkflowMakerForm',
'GenerateForm', 'InventoryList', 'CredentialList', '$q', 'GenerateForm', 'InventoryList', 'CredentialList', '$q',
function($scope, WorkflowHelpService, GenerateList, JobTemplateList, ProjectList, function($scope, WorkflowService, GenerateList, TemplateList, ProjectList,
GetBasePath, Wait, JobTemplateService, $state, GetBasePath, Wait, TemplatesService, $state,
ProcessErrors, InventorySourcesList, CreateSelect2, WorkflowMakerForm, ProcessErrors, InventorySourcesList, CreateSelect2, WorkflowMakerForm,
GenerateForm, InventoryList, CredentialList, $q) { GenerateForm, InventoryList, CredentialList, $q) {
@@ -59,11 +59,11 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'JobTemplateLis
$scope.closeWorkflowMaker = function() { $scope.closeWorkflowMaker = function() {
// Revert the data to the master which was created when the dialog was opened // Revert the data to the master which was created when the dialog was opened
$scope.treeData.data = angular.copy($scope.treeDataMaster); $scope.treeData.data = angular.copy($scope.treeDataMaster);
WorkflowHelpService.closeDialog(); $scope.closeDialog();
}; };
$scope.saveWorkflowMaker = function() { $scope.saveWorkflowMaker = function() {
WorkflowHelpService.closeDialog(); $scope.closeDialog();
}; };
/* ADD NODE FUNCTIONS */ /* ADD NODE FUNCTIONS */
@@ -78,7 +78,7 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'JobTemplateLis
$scope.addParent = parent; $scope.addParent = parent;
$scope.betweenTwoNodes = betweenTwoNodes; $scope.betweenTwoNodes = betweenTwoNodes;
$scope.placeholderNode = WorkflowHelpService.addPlaceholderNode({ $scope.placeholderNode = WorkflowService.addPlaceholderNode({
parent: parent, parent: parent,
betweenTwoNodes: betweenTwoNodes, betweenTwoNodes: betweenTwoNodes,
tree: $scope.treeData.data, tree: $scope.treeData.data,
@@ -87,7 +87,7 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'JobTemplateLis
$scope.treeData.nextIndex++; $scope.treeData.nextIndex++;
let siblingConnectionTypes = WorkflowHelpService.getSiblingConnectionTypes({ let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({
tree: $scope.treeData.data, tree: $scope.treeData.data,
parentId: betweenTwoNodes ? parent.source.id : parent.id parentId: betweenTwoNodes ? parent.source.id : parent.id
}); });
@@ -187,7 +187,7 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'JobTemplateLis
$scope.cancelNodeForm = function() { $scope.cancelNodeForm = function() {
if ($scope.workflowMakerFormConfig.nodeMode === "add") { if ($scope.workflowMakerFormConfig.nodeMode === "add") {
// Remove the placeholder node from the tree // Remove the placeholder node from the tree
WorkflowHelpService.removeNodeFromTree({ WorkflowService.removeNodeFromTree({
tree: $scope.treeData.data, tree: $scope.treeData.data,
nodeToBeDeleted: $scope.placeholderNode nodeToBeDeleted: $scope.placeholderNode
}); });
@@ -212,12 +212,12 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'JobTemplateLis
$scope.workflowMakerFormConfig.nodeMode = "edit"; $scope.workflowMakerFormConfig.nodeMode = "edit";
let parent = WorkflowHelpService.searchTree({ let parent = WorkflowService.searchTree({
element: $scope.treeData.data, element: $scope.treeData.data,
matchingId: nodeToEdit.parent.id matchingId: nodeToEdit.parent.id
}); });
$scope.nodeBeingEdited = WorkflowHelpService.searchTree({ $scope.nodeBeingEdited = WorkflowService.searchTree({
element: parent, element: parent,
matchingId: nodeToEdit.id matchingId: nodeToEdit.id
}); });
@@ -348,7 +348,7 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'JobTemplateLis
// This is a node that we got back from the api with an incomplete // 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 // unified job template so we're going to pull down the whole object
JobTemplateService.getUnifiedJobTemplate($scope.nodeBeingEdited.unifiedJobTemplate.id) TemplatesService.getUnifiedJobTemplate($scope.nodeBeingEdited.unifiedJobTemplate.id)
.then(function(data) { .then(function(data) {
$scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]); $scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]);
@@ -358,12 +358,12 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'JobTemplateLis
let retrievingInventory = false; let retrievingInventory = false;
if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_credential_on_launch && $scope.nodeBeingEdited.originalNodeObj.credential) { if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_credential_on_launch && $scope.nodeBeingEdited.originalNodeObj.credential) {
defers.push(JobTemplateService.getCredential($scope.nodeBeingEdited.originalNodeObj.credential)); defers.push(TemplatesService.getCredential($scope.nodeBeingEdited.originalNodeObj.credential));
retrievingCredential = true; retrievingCredential = true;
} }
if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_inventory_on_launch && $scope.nodeBeingEdited.originalNodeObj.inventory) { if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_inventory_on_launch && $scope.nodeBeingEdited.originalNodeObj.inventory) {
defers.push(JobTemplateService.getInventory($scope.nodeBeingEdited.originalNodeObj.inventory)); defers.push(TemplatesService.getInventory($scope.nodeBeingEdited.originalNodeObj.inventory));
retrievingInventory = true; retrievingInventory = true;
} }
@@ -427,7 +427,7 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'JobTemplateLis
// TODO: turn this into a promise so that we can handle errors // TODO: turn this into a promise so that we can handle errors
WorkflowHelpService.removeNodeFromTree({ WorkflowService.removeNodeFromTree({
tree: $scope.treeData.data, tree: $scope.treeData.data,
nodeToBeDeleted: $scope.nodeToBeDeleted nodeToBeDeleted: $scope.nodeToBeDeleted
}); });

View File

@@ -6,15 +6,15 @@
import workflowMakerController from './workflow-maker.controller'; import workflowMakerController from './workflow-maker.controller';
export default ['templateUrl', 'CreateDialog', 'Wait', export default ['templateUrl', 'CreateDialog', 'Wait', '$state',
function(templateUrl, CreateDialog, Wait) { function(templateUrl, CreateDialog, Wait, $state) {
return { return {
scope: { scope: {
treeData: '=', treeData: '=',
canAddWorkflowJobTemplate: '=' canAddWorkflowJobTemplate: '='
}, },
restrict: 'E', restrict: 'E',
templateUrl: templateUrl('job-templates/workflow-maker/workflow-maker'), templateUrl: templateUrl('templates/workflows/workflow-maker/workflow-maker'),
controller: workflowMakerController, controller: workflowMakerController,
link: function(scope) { link: function(scope) {
CreateDialog({ CreateDialog({
@@ -49,6 +49,12 @@ export default ['templateUrl', 'CreateDialog', 'Wait',
scope.$broadcast("refreshWorkflowChart"); scope.$broadcast("refreshWorkflowChart");
}); });
scope.closeDialog = function() {
$('#workflow-modal-dialog').dialog('destroy');
$state.go('^');
};
} }
}; };
} }

View File

@@ -0,0 +1,255 @@
export default [function(){
return {
searchTree: function(params) {
// params.element
// params.matchingId
if(params.element.id === 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
});
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";
}
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
};
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) {
placeholder.children.push(angular.copy(child));
parentNode.children[index] = placeholder;
placeholderRef = 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.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.placeholder && child.edgeType) {
siblingConnectionTypes[child.edgeType] = true;
}
});
}
return Object.keys(siblingConnectionTypes);
},
buildTree: function(params) {
//params.workflowNodes
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);
});
return treeData;
},
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,
unifiedJobTemplate: _.clone(params.nodesObj[params.nodeId].summary_fields.unified_job_template),
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;
}
// 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;
}
};
}];

View File

@@ -6,6 +6,7 @@ export default ['workflowData',
'$scope', '$scope',
'ParseTypeChange', 'ParseTypeChange',
'ParseVariableString', 'ParseVariableString',
'WorkflowService',
function(workflowData, function(workflowData,
workflowResultsService, workflowResultsService,
workflowDataOptions, workflowDataOptions,
@@ -13,176 +14,198 @@ export default ['workflowData',
workflowNodes, workflowNodes,
$scope, $scope,
ParseTypeChange, ParseTypeChange,
ParseVariableString ParseVariableString,
WorkflowService
) { ) {
var getTowerLinks = function() {
var getTowerLink = function(key) { var getTowerLinks = function() {
if ($scope.workflow.related[key]) { var getTowerLink = function(key) {
return '/#/' + $scope.workflow.related[key] if ($scope.workflow.related[key]) {
.split('api/v1/')[1]; return '/#/' + $scope.workflow.related[key]
} .split('api/v1/')[1];
else { }
return null; else {
} return null;
}
};
$scope.workflow_template_link = '/#/templates/workflow_job_template/'+$scope.workflow.workflow_job_template;
$scope.created_by_link = getTowerLink('created_by');
$scope.cloud_credential_link = getTowerLink('cloud_credential');
$scope.network_credential_link = getTowerLink('network_credential');
}; };
$scope.workflow_template_link = '/#/templates/workflow_job_template/'+$scope.workflow.workflow_job_template; var getTowerLabels = function() {
$scope.created_by_link = getTowerLink('created_by'); var getTowerLabel = function(key) {
$scope.cloud_credential_link = getTowerLink('cloud_credential'); if ($scope.workflowOptions && $scope.workflowOptions[key]) {
$scope.network_credential_link = getTowerLink('network_credential'); return $scope.workflowOptions[key].choices
}; .filter(val => val[0] === $scope.workflow[key])
.map(val => val[1])[0];
} else {
return null;
}
};
var getTowerLabels = function() { $scope.status_label = getTowerLabel('status');
var getTowerLabel = function(key) { $scope.type_label = getTowerLabel('job_type');
if ($scope.workflowOptions && $scope.workflowOptions[key]) { $scope.verbosity_label = getTowerLabel('verbosity');
return $scope.workflowOptions[key].choices
.filter(val => val[0] === $scope.workflow[key])
.map(val => val[1])[0];
} else {
return null;
}
}; };
$scope.status_label = getTowerLabel('status'); function init() {
$scope.type_label = getTowerLabel('job_type'); // put initially resolved request data on scope
$scope.verbosity_label = getTowerLabel('verbosity'); $scope.workflow = workflowData;
}; $scope.workflow_nodes = workflowNodes;
$scope.workflowOptions = workflowDataOptions.actions.GET;
$scope.labels = jobLabels;
// var getTotalHostCount = function(count) { // turn related api browser routes into tower routes
// return Object getTowerLinks();
// .keys(count).reduce((acc, i) => acc += count[i], 0);
// };
// put initially resolved request data on scope // use options labels to manipulate display of details
$scope.workflow = workflowData; getTowerLabels();
$scope.workflow_nodes = workflowNodes;
$scope.workflowOptions = workflowDataOptions.actions.GET;
$scope.labels = jobLabels;
// turn related api browser routes into tower routes // set up a read only code mirror for extra vars
getTowerLinks(); $scope.variables = ParseVariableString($scope.workflow.extra_vars);
$scope.parseType = 'yaml';
ParseTypeChange({ scope: $scope,
field_id: 'pre-formatted-variables',
readOnly: true });
// use options labels to manipulate display of details // Click binding for the expand/collapse button on the standard out log
getTowerLabels(); $scope.stdoutFullScreen = false;
// set up a read only code mirror for extra vars $scope.stdoutArr = [];
$scope.variables = ParseVariableString($scope.workflow.extra_vars);
$scope.parseType = 'yaml';
ParseTypeChange({ scope: $scope,
field_id: 'pre-formatted-variables',
readOnly: true });
// Click binding for the expand/collapse button on the standard out log $scope.treeData = WorkflowService.buildTree({
$scope.stdoutFullScreen = false; workflowNodes: workflowNodes
$scope.toggleStdoutFullscreen = function() { });
$scope.stdoutFullScreen = !$scope.stdoutFullScreen;
};
$scope.deleteJob = function() { // TODO: I think that the workflow chart directive (and eventually d3) is meddling with
workflowResultsService.deleteJob($scope.workflow); // 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 = [];
}
$scope.cancelJob = function() { $scope.canAddWorkflowJobTemplate = false;
workflowResultsService.cancelJob($scope.workflow);
};
$scope.relaunchJob = function() {
workflowResultsService.relaunchJob($scope);
};
$scope.stdoutArr = [];
// EVENT STUFF BELOW
// just putting the event queue on scope so it can be inspected in the
// console
// $scope.event_queue = eventQueue.queue;
// $scope.defersArr = eventQueue.populateDefers;
// This is where the async updates to the UI actually happen.
// Flow is event queue munging in the service -> $scope setting in here
// var processEvent = function(event) {
// // put the event in the queue
// eventQueue.populate(event).then(mungedEvent => {
// // make changes to ui based on the event returned from the queue
// if (mungedEvent.changes) {
// mungedEvent.changes.forEach(change => {
// // we've got a change we need to make to the UI!
// // update the necessary scope and make the change
// if (change === 'startTime' && !$scope.workflow.start) {
// $scope.workflow.start = mungedEvent.startTime;
// }
//
// if (change === 'count' && !$scope.countFinished) {
// // for all events that affect the host count,
// // update the status bar as well as the host
// // count badge
// $scope.count = mungedEvent.count;
// $scope.hostCount = getTotalHostCount(mungedEvent
// .count);
// }
//
// if (change === 'playCount') {
// $scope.playCount = mungedEvent.playCount;
// }
//
// if (change === 'taskCount') {
// $scope.taskCount = mungedEvent.taskCount;
// }
//
// if (change === 'finishedTime' && !$scope.workflow.finished) {
// $scope.workflow.finished = mungedEvent.finishedTime;
// }
//
// if (change === 'countFinished') {
// // the playbook_on_stats event actually lets
// // us know that we don't need to iteratively
// // look at event to update the host counts
// // any more.
// $scope.countFinished = true;
// }
//
// if(change === 'stdout'){
// angular
// .element(".JobResultsStdOut-stdoutContainer")
// .append($compile(mungedEvent
// .stdout)($scope));
// }
// });
// }
//
// // the changes have been processed in the ui, mark it in the queue
// eventQueue.markProcessed(event);
// });
// };
// PULL! grab completed event data and process each event
// TODO: implement retry logic in case one of these requests fails
// var getEvents = function(url) {
// workflowResultsService.getEvents(url)
// .then(events => {
// events.results.forEach(event => {
// // get the name in the same format as the data
// // coming over the websocket
// event.event_name = event.event;
// processEvent(event);
// });
// if (events.next) {
// getEvents(events.next);
// }
// });
// };
// getEvents($scope.job.related.job_events);
// // Processing of job_events messages from the websocket
// $scope.$on(`ws-job_events-${$scope.workflow.id}`, function(e, data) {
// processEvent(data);
// });
// Processing of job-status messages from the websocket
$scope.$on(`ws-jobs`, function(e, data) {
if (parseInt(data.unified_job_id, 10) === parseInt($scope.workflow.id,10)) {
$scope.workflow.status = data.status;
} }
});
// var getTotalHostCount = function(count) {
// return Object
// .keys(count).reduce((acc, i) => acc += count[i], 0);
// };
$scope.toggleStdoutFullscreen = function() {
$scope.stdoutFullScreen = !$scope.stdoutFullScreen;
};
$scope.deleteJob = function() {
workflowResultsService.deleteJob($scope.workflow);
};
$scope.cancelJob = function() {
workflowResultsService.cancelJob($scope.workflow);
};
$scope.relaunchJob = function() {
workflowResultsService.relaunchJob($scope);
};
// EVENT STUFF BELOW
// just putting the event queue on scope so it can be inspected in the
// console
// $scope.event_queue = eventQueue.queue;
// $scope.defersArr = eventQueue.populateDefers;
// This is where the async updates to the UI actually happen.
// Flow is event queue munging in the service -> $scope setting in here
// var processEvent = function(event) {
// // put the event in the queue
// eventQueue.populate(event).then(mungedEvent => {
// // make changes to ui based on the event returned from the queue
// if (mungedEvent.changes) {
// mungedEvent.changes.forEach(change => {
// // we've got a change we need to make to the UI!
// // update the necessary scope and make the change
// if (change === 'startTime' && !$scope.workflow.start) {
// $scope.workflow.start = mungedEvent.startTime;
// }
//
// if (change === 'count' && !$scope.countFinished) {
// // for all events that affect the host count,
// // update the status bar as well as the host
// // count badge
// $scope.count = mungedEvent.count;
// $scope.hostCount = getTotalHostCount(mungedEvent
// .count);
// }
//
// if (change === 'playCount') {
// $scope.playCount = mungedEvent.playCount;
// }
//
// if (change === 'taskCount') {
// $scope.taskCount = mungedEvent.taskCount;
// }
//
// if (change === 'finishedTime' && !$scope.workflow.finished) {
// $scope.workflow.finished = mungedEvent.finishedTime;
// }
//
// if (change === 'countFinished') {
// // the playbook_on_stats event actually lets
// // us know that we don't need to iteratively
// // look at event to update the host counts
// // any more.
// $scope.countFinished = true;
// }
//
// if(change === 'stdout'){
// angular
// .element(".JobResultsStdOut-stdoutContainer")
// .append($compile(mungedEvent
// .stdout)($scope));
// }
// });
// }
//
// // the changes have been processed in the ui, mark it in the queue
// eventQueue.markProcessed(event);
// });
// };
// PULL! grab completed event data and process each event
// TODO: implement retry logic in case one of these requests fails
// var getEvents = function(url) {
// workflowResultsService.getEvents(url)
// .then(events => {
// events.results.forEach(event => {
// // get the name in the same format as the data
// // coming over the websocket
// event.event_name = event.event;
// processEvent(event);
// });
// if (events.next) {
// getEvents(events.next);
// }
// });
// };
// getEvents($scope.job.related.job_events);
// // Processing of job_events messages from the websocket
// $scope.$on(`ws-job_events-${$scope.workflow.id}`, function(e, data) {
// processEvent(data);
// });
// Processing of job-status messages from the websocket
$scope.$on(`ws-jobs`, function(e, data) {
if (parseInt(data.unified_job_id, 10) === parseInt($scope.workflow.id,10)) {
$scope.workflow.status = data.status;
}
});
init();
}]; }];

View File

@@ -228,7 +228,7 @@
</div> </div>
</div> </div>
<workflow-status-bar></workflow-status-bar> <workflow-status-bar></workflow-status-bar>
<!-- <job-results-standard-out></job-results-standard-out> --> <workflow-chart tree-data="treeData.data" can-add-workflow-job-template="canAddWorkflowJobTemplate" class="WorkflowMaker-chart"></workflow-chart>
</div> </div>
</div> </div>

View File

@@ -1,11 +1,11 @@
'use strict'; 'use strict';
describe('Controller: JobTemplatesList', () => { describe('Controller: TemplatesList', () => {
// Setup // Setup
let scope, let scope,
rootScope, rootScope,
state, state,
JobTemplatesListController, TemplatesListController,
ClearScope, ClearScope,
GetChoices, GetChoices,
Alert, Alert,
@@ -14,13 +14,13 @@ describe('Controller: JobTemplatesList', () => {
rbacUiControlService, rbacUiControlService,
canAddDeferred, canAddDeferred,
q, q,
JobTemplateService, TemplatesService,
deleteWorkflowJobTemplateDeferred, deleteWorkflowJobTemplateDeferred,
deleteJobTemplateDeferred, deleteJobTemplateDeferred,
Dataset; Dataset;
beforeEach(angular.mock.module('Tower')); beforeEach(angular.mock.module('Tower'));
beforeEach(angular.mock.module('jobTemplates', ($provide) => { beforeEach(angular.mock.module('templates', ($provide) => {
state = jasmine.createSpyObj('state', [ state = jasmine.createSpyObj('state', [
'$get', '$get',
@@ -38,7 +38,7 @@ describe('Controller: JobTemplatesList', () => {
} }
}; };
JobTemplateService = { TemplatesService = {
deleteWorkflowJobTemplate: function(){ deleteWorkflowJobTemplate: function(){
return angular.noop; return angular.noop;
}, },
@@ -85,10 +85,10 @@ describe('Controller: JobTemplatesList', () => {
rbacUiControlService.canAdd = jasmine.createSpy('canAdd').and.returnValue(canAddDeferred.promise); rbacUiControlService.canAdd = jasmine.createSpy('canAdd').and.returnValue(canAddDeferred.promise);
JobTemplateService.deleteWorkflowJobTemplate = jasmine.createSpy('deleteWorkflowJobTemplate').and.returnValue(deleteWorkflowJobTemplateDeferred.promise); TemplatesService.deleteWorkflowJobTemplate = jasmine.createSpy('deleteWorkflowJobTemplate').and.returnValue(deleteWorkflowJobTemplateDeferred.promise);
JobTemplateService.deleteJobTemplate = jasmine.createSpy('deleteJobTemplate').and.returnValue(deleteJobTemplateDeferred.promise); TemplatesService.deleteJobTemplate = jasmine.createSpy('deleteJobTemplate').and.returnValue(deleteJobTemplateDeferred.promise);
JobTemplatesListController = $controller('JobTemplatesListController', { TemplatesListController = $controller('TemplatesListController', {
$scope: scope, $scope: scope,
$rootScope: rootScope, $rootScope: rootScope,
$state: state, $state: state,
@@ -98,7 +98,7 @@ describe('Controller: JobTemplatesList', () => {
Prompt: Prompt, Prompt: Prompt,
InitiatePlaybookRun: InitiatePlaybookRun, InitiatePlaybookRun: InitiatePlaybookRun,
rbacUiControlService: rbacUiControlService, rbacUiControlService: rbacUiControlService,
JobTemplateService: JobTemplateService, TemplatesService: TemplatesService,
Dataset: Dataset Dataset: Dataset
}); });
})); }));
@@ -163,7 +163,7 @@ describe('Controller: JobTemplatesList', () => {
expect(Prompt).toHaveBeenCalled(); expect(Prompt).toHaveBeenCalled();
}); });
it('should call JobTemplateService.deleteWorkflowJobTemplate when the user takes affirmative action on the delete modal and type = "Workflow Job Template"', ()=>{ it('should call TemplatesService.deleteWorkflowJobTemplate when the user takes affirmative action on the delete modal and type = "Workflow Job Template"', ()=>{
// Note that Prompt has been mocked up above to immediately call the callback function that gets passed in // Note that Prompt has been mocked up above to immediately call the callback function that gets passed in
// which is how we access the private function in the controller // which is how we access the private function in the controller
@@ -174,10 +174,10 @@ describe('Controller: JobTemplatesList', () => {
}; };
scope.deleteJobTemplate(testTemplate); scope.deleteJobTemplate(testTemplate);
expect(JobTemplateService.deleteWorkflowJobTemplate).toHaveBeenCalled(); expect(TemplatesService.deleteWorkflowJobTemplate).toHaveBeenCalled();
}); });
it('should call JobTemplateService.deleteJobTemplate when the user takes affirmative action on the delete modal and type = "Workflow Job Template"', ()=>{ it('should call TemplatesService.deleteJobTemplate when the user takes affirmative action on the delete modal and type = "Workflow Job Template"', ()=>{
// Note that Prompt has been mocked up above to immediately call the callback function that gets passed in // Note that Prompt has been mocked up above to immediately call the callback function that gets passed in
// which is how we access the private function in the controller // which is how we access the private function in the controller
@@ -188,7 +188,7 @@ describe('Controller: JobTemplatesList', () => {
}; };
scope.deleteJobTemplate(testTemplate); scope.deleteJobTemplate(testTemplate);
expect(JobTemplateService.deleteJobTemplate).toHaveBeenCalled(); expect(TemplatesService.deleteJobTemplate).toHaveBeenCalled();
}); });
}); });

View File

@@ -8,7 +8,7 @@ describe('Controller: WorkflowAdd', () => {
ClearScope, ClearScope,
Alert, Alert,
GenerateForm, GenerateForm,
JobTemplateService, TemplatesService,
q, q,
getLabelsDeferred, getLabelsDeferred,
createWorkflowJobTemplateDeferred, createWorkflowJobTemplateDeferred,
@@ -20,7 +20,7 @@ describe('Controller: WorkflowAdd', () => {
ToJSON; ToJSON;
beforeEach(angular.mock.module('Tower')); beforeEach(angular.mock.module('Tower'));
beforeEach(angular.mock.module('jobTemplates', ($provide) => { beforeEach(angular.mock.module('templates', ($provide) => {
state = jasmine.createSpyObj('state', [ state = jasmine.createSpyObj('state', [
'$get', '$get',
@@ -35,7 +35,7 @@ describe('Controller: WorkflowAdd', () => {
'applyDefaults' 'applyDefaults'
]); ]);
JobTemplateService = { TemplatesService = {
getLabelOptions: function(){ getLabelOptions: function(){
return angular.noop; return angular.noop;
}, },
@@ -79,8 +79,8 @@ describe('Controller: WorkflowAdd', () => {
ParseTypeChange = _ParseTypeChange_; ParseTypeChange = _ParseTypeChange_;
ToJSON = _ToJSON_; ToJSON = _ToJSON_;
JobTemplateService.getLabelOptions = jasmine.createSpy('getLabelOptions').and.returnValue(getLabelsDeferred.promise); TemplatesService.getLabelOptions = jasmine.createSpy('getLabelOptions').and.returnValue(getLabelsDeferred.promise);
JobTemplateService.createWorkflowJobTemplate = jasmine.createSpy('createWorkflowJobTemplate').and.returnValue(createWorkflowJobTemplateDeferred.promise); TemplatesService.createWorkflowJobTemplate = jasmine.createSpy('createWorkflowJobTemplate').and.returnValue(createWorkflowJobTemplateDeferred.promise);
WorkflowAdd = $controller('WorkflowAdd', { WorkflowAdd = $controller('WorkflowAdd', {
$scope: scope, $scope: scope,
@@ -88,7 +88,7 @@ describe('Controller: WorkflowAdd', () => {
ClearScope: ClearScope, ClearScope: ClearScope,
Alert: Alert, Alert: Alert,
GenerateForm: GenerateForm, GenerateForm: GenerateForm,
JobTemplateService: JobTemplateService, TemplatesService: TemplatesService,
ProcessErrors: ProcessErrors, ProcessErrors: ProcessErrors,
CreateSelect2: CreateSelect2, CreateSelect2: CreateSelect2,
Wait: Wait, Wait: Wait,
@@ -102,7 +102,7 @@ describe('Controller: WorkflowAdd', () => {
}); });
it('should get/set the label options and select2-ify the input', ()=>{ it('should get/set the label options and select2-ify the input', ()=>{
// Resolve JobTemplateService.getLabelsForJobTemplate // Resolve TemplatesService.getLabelsForJobTemplate
getLabelsDeferred.resolve({ getLabelsDeferred.resolve({
foo: "bar" foo: "bar"
}); });
@@ -120,7 +120,7 @@ describe('Controller: WorkflowAdd', () => {
}); });
it('should call ProcessErrors when getLabelsForJobTemplate returns a rejected promise', ()=>{ it('should call ProcessErrors when getLabelsForJobTemplate returns a rejected promise', ()=>{
// Reject JobTemplateService.getLabelsForJobTemplate // Reject TemplatesService.getLabelsForJobTemplate
getLabelsDeferred.reject({ getLabelsDeferred.reject({
data: "mockedData", data: "mockedData",
status: 400 status: 400
@@ -133,11 +133,11 @@ describe('Controller: WorkflowAdd', () => {
describe('scope.formSave()', () => { describe('scope.formSave()', () => {
it('should call JobTemplateService.createWorkflowJobTemplate', ()=>{ it('should call TemplatesService.createWorkflowJobTemplate', ()=>{
scope.name = "Test Workflow"; scope.name = "Test Workflow";
scope.description = "This is a test description"; scope.description = "This is a test description";
scope.formSave(); scope.formSave();
expect(JobTemplateService.createWorkflowJobTemplate).toHaveBeenCalledWith({ expect(TemplatesService.createWorkflowJobTemplate).toHaveBeenCalledWith({
name: "Test Workflow", name: "Test Workflow",
description: "This is a test description", description: "This is a test description",
labels: undefined, labels: undefined,

View File

@@ -7,7 +7,7 @@ describe('Controller: WorkflowMaker', () => {
WorkflowHelpService; WorkflowHelpService;
beforeEach(angular.mock.module('Tower')); beforeEach(angular.mock.module('Tower'));
beforeEach(angular.mock.module('jobTemplates', ($provide) => { beforeEach(angular.mock.module('templates', ($provide) => {
WorkflowHelpService = jasmine.createSpyObj('WorkflowHelpService', [ WorkflowHelpService = jasmine.createSpyObj('WorkflowHelpService', [
'closeDialog', 'closeDialog',
@@ -21,6 +21,7 @@ describe('Controller: WorkflowMaker', () => {
beforeEach(angular.mock.inject( ($rootScope, $controller, _WorkflowHelpService_) => { beforeEach(angular.mock.inject( ($rootScope, $controller, _WorkflowHelpService_) => {
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.closeDialog = jasmine.createSpy();
scope.treeData = { scope.treeData = {
data: { data: {
id: 1, id: 1,
@@ -50,7 +51,7 @@ describe('Controller: WorkflowMaker', () => {
it('should close the dialog', ()=>{ it('should close the dialog', ()=>{
scope.saveWorkflowMaker(); scope.saveWorkflowMaker();
expect(WorkflowHelpService.closeDialog).toHaveBeenCalled(); expect(scope.closeDialog).toHaveBeenCalled();
}); });
}); });