mirror of
https://github.com/ansible/awx.git
synced 2026-03-19 09:57:33 -02:30
implement model-based copy for job templates and workflow templates
This commit is contained in:
@@ -1,148 +1,75 @@
|
|||||||
/** ***********************************************
|
/** ***********************************************
|
||||||
* Copyright (c) 2017 Ansible, Inc.
|
* Copyright (c) 2018 Ansible, Inc.
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
************************************************ */
|
************************************************ */
|
||||||
|
|
||||||
const ALERT_MISSING = 'Template parameter is missing';
|
|
||||||
const ALERT_NO_PERMISSION = 'You do not have permission to perform this action.';
|
|
||||||
const ALERT_UNKNOWN = 'We were unable to determine this template\'s type';
|
|
||||||
const ALERT_UNKNOWN_COPY = `${ALERT_UNKNOWN} while copying.`;
|
|
||||||
const ALERT_UNKNOWN_DELETE = `${ALERT_UNKNOWN} while deleting.`;
|
|
||||||
const ALERT_UNKNOWN_EDIT = `${ALERT_UNKNOWN} while routing to edit.`;
|
|
||||||
const ALERT_UNKNOWN_LAUNCH = `${ALERT_UNKNOWN} while launching.`;
|
|
||||||
const ALERT_UNKNOWN_SCHEDULE = `${ALERT_UNKNOWN} while routing to schedule.`;
|
|
||||||
const ERROR_EDIT = 'Error: Unable to edit template';
|
|
||||||
const ERROR_DELETE = 'Error: Unable to delete template';
|
|
||||||
const ERROR_LAUNCH = 'Error: Unable to launch template';
|
|
||||||
const ERROR_UNKNOWN = 'Error: Unable to determine template type';
|
|
||||||
const ERROR_JOB_SCHEDULE = 'Error: Unable to schedule job';
|
|
||||||
const ERROR_TEMPLATE_COPY = 'Error: Unable to copy job template';
|
|
||||||
const ERROR_WORKFLOW_COPY = 'Error: Unable to copy workflow job template';
|
|
||||||
|
|
||||||
const JOB_TEMPLATE_ALIASES = ['job_template', 'Job Template'];
|
const JOB_TEMPLATE_ALIASES = ['job_template', 'Job Template'];
|
||||||
const WORKFLOW_TEMPLATE_ALIASES = ['workflow_job_template', 'Workflow Job Template'];
|
const WORKFLOW_TEMPLATE_ALIASES = ['workflow_job_template', 'Workflow Job Template'];
|
||||||
|
|
||||||
const isJobTemplate = obj => _.includes(JOB_TEMPLATE_ALIASES, _.get(obj, 'type'));
|
const isJobTemplate = ({ type }) => JOB_TEMPLATE_ALIASES.indexOf(type) > -1;
|
||||||
const isWorkflowTemplate = obj => _.includes(WORKFLOW_TEMPLATE_ALIASES, _.get(obj, 'type'));
|
const isWorkflowTemplate = ({ type }) => WORKFLOW_TEMPLATE_ALIASES.indexOf(type) > -1;
|
||||||
|
const mapChoices = choices => Object.assign(...choices.map(([k, v]) => ({[k]: v})));
|
||||||
|
|
||||||
function TemplatesListController (
|
function ListTemplatesController(
|
||||||
$scope,
|
|
||||||
$rootScope,
|
|
||||||
Alert,
|
|
||||||
TemplateList,
|
|
||||||
Prompt,
|
|
||||||
ProcessErrors,
|
|
||||||
GetBasePath,
|
|
||||||
InitiatePlaybookRun,
|
|
||||||
Wait,
|
|
||||||
$state,
|
|
||||||
$filter,
|
$filter,
|
||||||
|
$scope,
|
||||||
|
$state,
|
||||||
|
Alert,
|
||||||
Dataset,
|
Dataset,
|
||||||
rbacUiControlService,
|
InitiatePlaybookRun,
|
||||||
TemplatesService,
|
ProcessErrors,
|
||||||
qs,
|
Prompt,
|
||||||
i18n,
|
|
||||||
JobTemplate,
|
|
||||||
WorkflowJobTemplate,
|
|
||||||
TemplatesStrings,
|
|
||||||
$q,
|
|
||||||
Empty,
|
|
||||||
i18n,
|
|
||||||
PromptService,
|
PromptService,
|
||||||
|
resolvedModels,
|
||||||
|
strings,
|
||||||
|
Wait,
|
||||||
) {
|
) {
|
||||||
const jobTemplate = new JobTemplate();
|
const vm = this || {};
|
||||||
const list = TemplateList;
|
const [jobTemplate, workflowTemplate] = resolvedModels;
|
||||||
|
|
||||||
init();
|
const choices = workflowTemplate.options('actions.GET.type.choices')
|
||||||
|
.concat(jobTemplate.options('actions.GET.type.choices'));
|
||||||
|
|
||||||
function init () {
|
vm.strings = strings;
|
||||||
$scope.canAdd = false;
|
vm.templateTypes = mapChoices(choices);
|
||||||
|
vm.activeId = parseInt($state.params.job_template_id || $state.params.workflow_template_id);
|
||||||
|
|
||||||
rbacUiControlService.canAdd('job_templates').then(params => {
|
$scope.canAddJobTemplate = jobTemplate.options('actions.POST')
|
||||||
$scope.canAddJobTemplate = params.canAdd;
|
$scope.canAddWorkflowJobTemplate = workflowTemplate.options('actions.POST')
|
||||||
});
|
$scope.canAdd = ($scope.canAddJobTemplate || $scope.canAddWorkflowJobTemplate);
|
||||||
|
|
||||||
rbacUiControlService.canAdd('workflow_job_templates').then(params => {
|
// smart-search
|
||||||
$scope.canAddWorkflowJobTemplate = params.canAdd;
|
const name = 'templates';
|
||||||
});
|
const iterator = 'template';
|
||||||
|
const key = 'template_dataset';
|
||||||
|
|
||||||
// search init
|
$scope.list = { iterator, name };
|
||||||
$scope.list = list;
|
$scope.collection = { iterator, basePath: 'unified_job_templates' };
|
||||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
$scope[key] = Dataset.data;
|
||||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
$scope[name] = Dataset.data.results;
|
||||||
$scope.options = {};
|
$scope.$on('updateDataset', (e, dataset) => {
|
||||||
|
$scope[key] = dataset;
|
||||||
$rootScope.flashMessage = null;
|
$scope[name] = dataset.results;
|
||||||
}
|
|
||||||
|
|
||||||
$scope.$on(`${list.iterator}_options`, (event, data) => {
|
|
||||||
$scope.options = data.data.actions.GET;
|
|
||||||
optionsRequestDataProcessing();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.$watchCollection('templates', () => {
|
vm.runTemplate = template => {
|
||||||
optionsRequestDataProcessing();
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.$on('ws-jobs', () => {
|
|
||||||
const path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
|
||||||
qs.search(path, $state.params[`${list.iterator}_search`])
|
|
||||||
.then(searchResponse => {
|
|
||||||
$scope[`${list.iterator}_dataset`] = searchResponse.data;
|
|
||||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// iterate over the list and add fields like type label, after the
|
|
||||||
// OPTIONS request returns, or the list is sorted/paginated/searched
|
|
||||||
function optionsRequestDataProcessing () {
|
|
||||||
$scope[list.name].forEach((item, idx) => {
|
|
||||||
const itm = $scope[list.name][idx];
|
|
||||||
// Set the item type label
|
|
||||||
if (list.fields.type && _.has($scope.options, 'type.choices')) {
|
|
||||||
$scope.options.type.choices.forEach(choice => {
|
|
||||||
if (choice[0] === item.type) {
|
|
||||||
[itm.type_label] = choice;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.editJobTemplate = template => {
|
|
||||||
if (!template) {
|
if (!template) {
|
||||||
Alert(ERROR_EDIT, ALERT_MISSING);
|
Alert(strings.get('error.LAUNCH'), strings.get('alert.MISSING_PARAMETER'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJobTemplate(template)) {
|
if (isJobTemplate(template)) {
|
||||||
$state.transitionTo('templates.editJobTemplate', { job_template_id: template.id });
|
runJobTemplate(template);
|
||||||
} else if (isWorkflowTemplate(template)) {
|
} else if (isWorkflowTemplate(template)) {
|
||||||
$state.transitionTo('templates.editWorkflowJobTemplate', { workflow_job_template_id: template.id });
|
runWorkflowTemplate(template);
|
||||||
} else {
|
} else {
|
||||||
Alert(ERROR_UNKNOWN, ALERT_UNKNOWN_EDIT);
|
Alert(strings.get('error.UNKNOWN'), strings.get('alert.UNKNOWN_LAUNCH'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.submitJob = template => {
|
vm.scheduleTemplate = template => {
|
||||||
if (!template) {
|
if (!template) {
|
||||||
Alert(ERROR_LAUNCH, ALERT_MISSING);
|
Alert(strings.get('error.SCHEDULE'), strings.get('alert.MISSING_PARAMETER'));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isJobTemplate(template)) {
|
|
||||||
submitJobTemplate(template)
|
|
||||||
} else if (isWorkflowTemplate(template)) {
|
|
||||||
InitiatePlaybookRun({ scope: $scope, id: template.id, job_type: 'workflow_job_template' });
|
|
||||||
} else {
|
|
||||||
Alert(ERROR_UNKNOWN, ALERT_UNKNOWN_LAUNCH);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.scheduleJob = template => {
|
|
||||||
if (!template) {
|
|
||||||
Alert(ERROR_JOB_SCHEDULE, ALERT_MISSING);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,362 +78,329 @@ function TemplatesListController (
|
|||||||
} else if (isWorkflowTemplate(template)) {
|
} else if (isWorkflowTemplate(template)) {
|
||||||
$state.go('workflowJobTemplateSchedules', { id: template.id });
|
$state.go('workflowJobTemplateSchedules', { id: template.id });
|
||||||
} else {
|
} else {
|
||||||
Alert(ERROR_UNKNOWN, ALERT_UNKNOWN_SCHEDULE);
|
Alert(strings.get('error.UNKNOWN'), strings.get('alert.UNKNOWN_SCHEDULE'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.deleteJobTemplate = template => {
|
vm.deleteTemplate = template => {
|
||||||
if (!template) {
|
if (!template) {
|
||||||
Alert(ERROR_DELETE, ALERT_MISSING);
|
Alert(strings.get('error.DELETE'), strings.get('alert.MISSING_PARAMETER'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWorkflowTemplate(template)) {
|
if (isWorkflowTemplate(template)) {
|
||||||
const body = TemplatesStrings.get('deleteResource.CONFIRM', 'workflow job template');
|
displayWorkflowTemplateDeletePrompt(template);
|
||||||
$scope.displayTemplateDeletePrompt(template, body);
|
|
||||||
} else if (isJobTemplate(template)) {
|
} else if (isJobTemplate(template)) {
|
||||||
jobTemplate.getDependentResourceCounts(template.id)
|
jobTemplate.getDependentResourceCounts(template.id)
|
||||||
.then(counts => {
|
.then(counts => displayJobTemplateDeletePrompt(template, counts));
|
||||||
const body = buildTemplateDeletePromptHTML(counts);
|
|
||||||
$scope.displayTemplateDeletePrompt(template, body);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
Alert(ERROR_UNKNOWN, ALERT_UNKNOWN_DELETE);
|
Alert(strings.get('error.UNKNOWN'), strings.get('alert.UNKNOWN_DELETE'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.copyTemplate = template => {
|
vm.copyTemplate = template => {
|
||||||
if (!template) {
|
if (!template) {
|
||||||
Alert(ERROR_TEMPLATE_COPY, ALERT_MISSING);
|
Alert(strings.get('error.COPY'), strings.get('alert.MISSING_PARAMETER'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJobTemplate(template)) {
|
if (isJobTemplate(template)) {
|
||||||
$scope.copyJobTemplate(template);
|
copyJobTemplate(template);
|
||||||
} else if (isWorkflowTemplate(template)) {
|
} else if (isWorkflowTemplate(template)) {
|
||||||
$scope.copyWorkflowJobTemplate(template);
|
copyWorkflowTemplate(template);
|
||||||
} else {
|
} else {
|
||||||
Alert(ERROR_UNKNOWN, ALERT_UNKNOWN_COPY);
|
Alert(strings.get('error.UNKNOWN'), strings.get('alert.UNKNOWN_COPY'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.copyJobTemplate = template => {
|
vm.getModified = template => {
|
||||||
|
const modified = _.get(template, 'modified');
|
||||||
|
|
||||||
|
if (!modified) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = $filter('longDate')(modified);
|
||||||
|
|
||||||
|
const { username, id } = _.get(template, 'summary_fields.modified_by', {});
|
||||||
|
|
||||||
|
if (username && id) {
|
||||||
|
html += ` by <a href="/#/users/${id}">${$filter('sanitize')(username)}</a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.getLastRan = template => {
|
||||||
|
const lastJobRun = _.get(template, 'last_job_run');
|
||||||
|
|
||||||
|
if (!lastJobRun) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = $filter('longDate')(modified);
|
||||||
|
|
||||||
|
// TODO: uncomment and update when last job run user is returned by api
|
||||||
|
// const { username, id } = _.get(template, 'summary_fields.last_job_run_by', {});
|
||||||
|
|
||||||
|
// if (username && id) {
|
||||||
|
// html += ` by <a href="/#/users/${id}">${$filter('sanitize')(username)}</a>`;
|
||||||
|
//}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
||||||
|
function createErrorHandler(path, action) {
|
||||||
|
return ({ data, status }) => {
|
||||||
|
const hdr = strings.get('error.HEADER');
|
||||||
|
const msg = strings.get('error.CALL', { path, action, status });
|
||||||
|
ProcessErrors($scope, data, status, null, { hdr, msg });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyJobTemplate(template) {
|
||||||
Wait('start');
|
Wait('start');
|
||||||
new JobTemplate('get', template.id)
|
jobTemplate
|
||||||
|
.create('get', template.id)
|
||||||
.then(model => model.copy())
|
.then(model => model.copy())
|
||||||
.then(({ id }) => {
|
.then(({ id }) => {
|
||||||
const params = { job_template_id: id };
|
const params = { job_template_id: id };
|
||||||
$state.go('templates.editJobTemplate', params, { reload: true });
|
$state.go('templates.editJobTemplate', params, { reload: true });
|
||||||
})
|
})
|
||||||
.catch(({ data, status }) => {
|
.catch(createErrorHandler('copy job template', 'POST'))
|
||||||
const params = { hdr: 'Error!', msg: `Call to copy failed. Return status: ${status}` };
|
|
||||||
ProcessErrors($scope, data, status, null, params);
|
|
||||||
})
|
|
||||||
.finally(() => Wait('stop'));
|
.finally(() => Wait('stop'));
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.copyWorkflowJobTemplate = template => {
|
function copyWorkflowTemplate(template) {
|
||||||
Wait('start');
|
Wait('start');
|
||||||
new WorkflowJobTemplate('get', template.id)
|
workflowTemplate
|
||||||
.then(model => model.extend('GET', 'copy'))
|
.create('get', template.id)
|
||||||
|
.then(model => model.extend('get', 'copy'))
|
||||||
.then(model => {
|
.then(model => {
|
||||||
const action = () => {
|
const action = () => {
|
||||||
|
Wait('start');
|
||||||
model.copy()
|
model.copy()
|
||||||
.then(({ id }) => {
|
.then(({ id }) => {
|
||||||
const params = { workflow_job_template_id: id };
|
const params = { workflow_job_template_id: id };
|
||||||
$state.go('templates.editWorkflowJobTemplate', params, { reload: true });
|
$state.go('templates.editWorkflowJobTemplate', params, { reload: true });
|
||||||
})
|
})
|
||||||
.catch(({ data, status }) => {
|
.catch(createErrorHandler('copy workflow', 'POST'))
|
||||||
const params = { hdr: 'Error!', msg: `Call to copy failed. Return status: ${status}` };
|
.finally(() => Wait('stop'));
|
||||||
ProcessErrors($scope, data, status, null, params);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (model.get('related.copy.can_copy_without_user_input')) {
|
if (model.get('related.copy.can_copy_without_user_input')) {
|
||||||
action();
|
action();
|
||||||
} else if (model.get('related.copy.can_copy')) {
|
} else if (model.get('related.copy.can_copy')) {
|
||||||
Prompt({
|
Prompt({
|
||||||
hdr: 'Copy Workflow',
|
|
||||||
action,
|
action,
|
||||||
actionText: 'COPY',
|
actionText: strings.get('COPY'),
|
||||||
|
body: buildWorkflowCopyPromptHTML(model.get('related.copy')),
|
||||||
class: 'Modal-primaryButton',
|
class: 'Modal-primaryButton',
|
||||||
body: buildWorkflowCopyPromptHTML(model.get('related.copy'))
|
hdr: strings.get('actions.COPY_WORKFLOW'),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Alert(ERROR_WORKFLOW_COPY, ALERT_NO_PERMISSION);
|
Alert(strings.get('error.COPY'), strings.get('alert.NO_PERMISSION'));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(({ data, status }) => {
|
.catch(createErrorHandler('copy workflow', 'GET'))
|
||||||
const params = { hdr: 'Error!', msg: `Call to copy failed. Return status: ${status}` };
|
|
||||||
ProcessErrors($scope, data, status, null, params);
|
|
||||||
})
|
|
||||||
.finally(() => Wait('stop'));
|
.finally(() => Wait('stop'));
|
||||||
};
|
|
||||||
|
|
||||||
$scope.displayTemplateDeletePrompt = (template, body) => {
|
|
||||||
const action = () => {
|
|
||||||
function handleSuccessfulDelete (isWorkflow) {
|
|
||||||
let reloadListStateParams = null;
|
|
||||||
let stateParamID;
|
|
||||||
|
|
||||||
if (isWorkflow) {
|
|
||||||
stateParamID = $state.params.workflow_job_template_id;
|
|
||||||
} else {
|
|
||||||
stateParamID = $state.params.job_template_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const templateSearch = _.get($state.params, 'template_search');
|
|
||||||
const { page } = templateSearch;
|
|
||||||
|
|
||||||
if ($scope.templates.length === 1 && !_.isEmpty(page) && page !== '1') {
|
|
||||||
reloadListStateParams = _.cloneDeep($state.params);
|
|
||||||
|
|
||||||
const pageNum = (parseInt(reloadListStateParams.template_search.page, 0) - 1);
|
|
||||||
reloadListStateParams.template_search.page = pageNum.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parseInt(stateParamID, 0) === template.id) {
|
|
||||||
$state.go('templates', reloadListStateParams, { reload: true });
|
|
||||||
} else {
|
|
||||||
$state.go('.', reloadListStateParams, { reload: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
Wait('stop');
|
|
||||||
} // end handler
|
|
||||||
|
|
||||||
let deleteServiceMethod;
|
|
||||||
let failMsg;
|
|
||||||
|
|
||||||
if (isWorkflowTemplate(template)) {
|
|
||||||
deleteServiceMethod = TemplatesService.deleteWorkflowJobTemplate;
|
|
||||||
failMsg = 'Call to delete workflow job template failed. DELETE returned status: ';
|
|
||||||
} else if (isJobTemplate(template)) {
|
|
||||||
deleteServiceMethod = TemplatesService.deleteJobTemplate;
|
|
||||||
failMsg = 'Call to delete job template failed. DELETE returned status: ';
|
|
||||||
} else {
|
|
||||||
Alert(ERROR_UNKNOWN, ALERT_UNKNOWN_DELETE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#prompt-modal').modal('hide');
|
|
||||||
Wait('start');
|
|
||||||
|
|
||||||
deleteServiceMethod(template.id)
|
|
||||||
.then(() => handleSuccessfulDelete(isWorkflowTemplate(template)))
|
|
||||||
.catch(res => {
|
|
||||||
ProcessErrors($scope, res.data, res.status, null, {
|
|
||||||
hdr: 'Error!',
|
|
||||||
msg: `${failMsg} ${res.status}.`
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}; // end action
|
|
||||||
|
|
||||||
Prompt({
|
|
||||||
action,
|
|
||||||
actionText: 'DELETE',
|
|
||||||
body,
|
|
||||||
hdr: i18n._('Delete'),
|
|
||||||
resourceName: $filter('sanitize')(template.name)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function buildTemplateDeletePromptHTML (dependentResourceCounts) {
|
|
||||||
const invalidateRelatedLines = [];
|
|
||||||
|
|
||||||
let bodyHTML = `
|
|
||||||
<div class="Prompt-bodyQuery">
|
|
||||||
${TemplatesStrings.get('deleteResource.CONFIRM', 'job template')}
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
dependentResourceCounts.forEach(countObj => {
|
|
||||||
if (countObj.count && countObj.count > 0) {
|
|
||||||
invalidateRelatedLines.push(`<div>
|
|
||||||
<span class="Prompt-warningResourceTitle">
|
|
||||||
${countObj.label}
|
|
||||||
</span>
|
|
||||||
<span class="badge List-titleBadge">
|
|
||||||
${countObj.count}
|
|
||||||
</span>
|
|
||||||
</div>`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (invalidateRelatedLines && invalidateRelatedLines.length > 0) {
|
|
||||||
bodyHTML = `
|
|
||||||
<div class="Prompt-bodyQuery">
|
|
||||||
${TemplatesStrings.get('deleteResource.USED_BY', 'job template')}
|
|
||||||
${TemplatesStrings.get('deleteResource.CONFIRM', 'job template')}
|
|
||||||
</div>`;
|
|
||||||
invalidateRelatedLines.forEach(invalidateRelatedLine => {
|
|
||||||
bodyHTML += invalidateRelatedLine;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return bodyHTML;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildWorkflowCopyPromptHTML (data) {
|
function handleSuccessfulDelete(template) {
|
||||||
const {
|
const { page } = _.get($state.params, 'template_search');
|
||||||
credentials_unable_to_copy,
|
let reloadListStateParams = null;
|
||||||
inventories_unable_to_copy,
|
|
||||||
job_templates_unable_to_copy
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
let itemsHTML = '';
|
if ($scope.templates.length === 1 && !_.isEmpty(page) && page !== '1') {
|
||||||
|
reloadListStateParams = _.cloneDeep($state.params);
|
||||||
if (job_templates_unable_to_copy.length > 0) {
|
const pageNumber = (parseInt(reloadListStateParams.template_search.page, 0) - 1);
|
||||||
itemsHTML += '<div>Unified Job Templates that cannot be copied<ul>';
|
reloadListStateParams.template_search.page = pageNumber.toString();
|
||||||
_.forOwn(job_templates_unable_to_copy, ujt => {
|
|
||||||
if (ujt) {
|
|
||||||
itemsHTML += `<li>'${ujt}</li>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
itemsHTML += '</ul></div>';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inventories_unable_to_copy.length > 0) {
|
if (parseInt($state.params.job_template_id, 0) === template.id) {
|
||||||
itemsHTML += '<div>Node prompted inventories that cannot be copied<ul>';
|
$state.go('templates', reloadListStateParams, { reload: true });
|
||||||
_.forOwn(inventories_unable_to_copy, inv => {
|
} else if (parseInt($state.params.workflow_job_template_id, 0) === template.id) {
|
||||||
if (inv) {
|
$state.go('templates', reloadListStateParams, { reload: true });
|
||||||
itemsHTML += `<li>'${inv}</li>`;
|
} else {
|
||||||
}
|
$state.go('.', reloadListStateParams, { reload: true });
|
||||||
});
|
|
||||||
itemsHTML += '</ul></div>';
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (credentials_unable_to_copy.length > 0) {
|
function displayJobTemplateDeletePrompt(template, counts) {
|
||||||
itemsHTML += '<div>Node prompted credentials that cannot be copied<ul>';
|
Prompt({
|
||||||
_.forOwn(credentials_unable_to_copy, cred => {
|
action() {
|
||||||
if (cred) {
|
$('#prompt-modal').modal('hide');
|
||||||
itemsHTML += `<li>'${cred}</li>`;
|
Wait('start');
|
||||||
}
|
jobTemplate
|
||||||
});
|
.request('delete', template.id)
|
||||||
itemsHTML += '</ul></div>';
|
.then(() => handleSuccessfulDelete(template))
|
||||||
}
|
.catch(createErrorHandler('delete template', 'DELETE'))
|
||||||
|
.finally(() => Wait('stop'));
|
||||||
|
},
|
||||||
|
hdr: strings.get('DELETE'),
|
||||||
|
resourceName: $filter('sanitize')(template.name),
|
||||||
|
body: buildJobTemplateDeletePromptHTML(counts),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const bodyHTML = `
|
function displayWorkflowTemplateDeletePrompt(template) {
|
||||||
|
Prompt({
|
||||||
|
action() {
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
Wait('start');
|
||||||
|
workflowTemplate
|
||||||
|
.request('delete', template.id)
|
||||||
|
.then(() => handleSuccessfulDelete(template))
|
||||||
|
.catch(createErrorHandler('delete template', 'DELETE'))
|
||||||
|
.finally(() => Wait('stop'))
|
||||||
|
},
|
||||||
|
hdr: strings.get('DELETE'),
|
||||||
|
resourceName: $filter('sanitize')(template.name),
|
||||||
|
body: strings.get('deleteResource.CONFIRM', 'workflow template'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildJobTemplateDeletePromptHTML(counts) {
|
||||||
|
const buildCount = count => `<span class="badge List-titleBadge">${count}</span>`;
|
||||||
|
const buildLabel = label => `<span class="Prompt-warningResourceTitle">
|
||||||
|
${$filter('sanitize')(label)}</span>`;
|
||||||
|
const buildCountLabel = ({ count, label }) => `<div>
|
||||||
|
${buildLabel(label)}${buildCount(count)}</div>`;
|
||||||
|
|
||||||
|
const displayedCounts = counts.filter(({ count }) => count > 0);
|
||||||
|
|
||||||
|
const html = `
|
||||||
|
${displayedCounts.length ? strings.get('deleteResource.USED_BY', 'job template') : ''}
|
||||||
|
${strings.get('deleteResource.CONFIRM', 'job template')}
|
||||||
|
${displayedCounts.map(buildCountLabel).join('')}
|
||||||
|
`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildWorkflowCopyPromptHTML(data) {
|
||||||
|
const pull = (data, param) => _.get(data, param, []).map($filter('sanitize'));
|
||||||
|
|
||||||
|
const credentials = pull(data, 'credentials_unable_to_copy');
|
||||||
|
const inventories = pull(data, 'inventories_unable_to_copy');
|
||||||
|
const templates = pull(data, 'templates_unable_to_copy');
|
||||||
|
|
||||||
|
const html = `
|
||||||
<div class="Prompt-bodyQuery">
|
<div class="Prompt-bodyQuery">
|
||||||
You do not have access to all resources used by this workflow.
|
${strings.get('warnings.WORKFLOW_RESTRICTED_COPY')}
|
||||||
Resources that you don't have access to will not be copied
|
|
||||||
and will result in an incomplete workflow.
|
|
||||||
</div>
|
</div>
|
||||||
<div class="Prompt-bodyTarget">
|
<div class="Prompt-bodyTarget">
|
||||||
${itemsHTML}
|
${templates.length ? `<div>Unified Job Templates<ul>` : ''}
|
||||||
|
${templates.map(item => `<li>${item}</li>`).join('')}
|
||||||
|
${templates.length ? `</ul></div>` : ''}
|
||||||
|
</div>
|
||||||
|
<div class="Prompt-bodyTarget">
|
||||||
|
${credentials.length ? `<div>Credentials<ul>` : ''}
|
||||||
|
${credentials.map(item => `<li>${item}</li>`).join('')}
|
||||||
|
${credentials.length ? `</ul></div>` : ''}
|
||||||
|
</div>
|
||||||
|
<div class="Prompt-bodyTarget">
|
||||||
|
${inventories.length ? `<div>Inventories<ul>` : ''}
|
||||||
|
${inventories.map(item => `<li>${item}</li>`).join('')}
|
||||||
|
${inventories.length ? `</ul></div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return bodyHTML;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitJobTemplate(template) {
|
function runJobTemplate(template) {
|
||||||
if(template) {
|
const selectedJobTemplate = jobTemplate.create();
|
||||||
if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) {
|
const preLaunchPromises = [
|
||||||
let jobTemplate = new JobTemplate();
|
selectedJobTemplate.getLaunch(template.id),
|
||||||
|
selectedJobTemplate.optionsLaunch(template.id),
|
||||||
|
];
|
||||||
|
|
||||||
$q.all([jobTemplate.optionsLaunch(template.id), jobTemplate.getLaunch(template.id)])
|
Promise.all(preLaunchPromises)
|
||||||
.then((responses) => {
|
.then(([launchData, launchOptions]) => {
|
||||||
if(jobTemplate.canLaunchWithoutPrompt()) {
|
if (selectedJobTemplate.canLaunchWithoutPrompt()) {
|
||||||
jobTemplate.postLaunch({id: template.id})
|
return selectedJobTemplate
|
||||||
.then((launchRes) => {
|
.postLaunch({ id: template.id })
|
||||||
$state.go('jobResult', { id: launchRes.data.job }, { reload: true });
|
.then(({ data }) => {
|
||||||
});
|
$state.go('jobResult', { id: data.job }, { reload: true })
|
||||||
} else {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if(responses[1].data.survey_enabled) {
|
const promptData = {
|
||||||
|
launchConf: launchData.data,
|
||||||
|
launchOptions: launchOptions.data,
|
||||||
|
template: template.id,
|
||||||
|
prompts: PromptService.processPromptValues({
|
||||||
|
launchConf: launchData.data,
|
||||||
|
launchOptions: launchOptions.data
|
||||||
|
}),
|
||||||
|
triggerModalOpen: true,
|
||||||
|
};
|
||||||
|
|
||||||
// go out and get the survey questions
|
if (launchData.data.survey_enabled) {
|
||||||
jobTemplate.getSurveyQuestions(template.id)
|
selectedJobTemplate.getSurveyQuestions(template.id)
|
||||||
.then((surveyQuestionRes) => {
|
.then(({ data }) => {
|
||||||
|
const processed = PromptService.processSurveyQuestions({ surveyQuestions: data.spec });
|
||||||
|
promptData.surveyQuestions = processed.surveyQuestions;
|
||||||
|
$scope.promptData = promptData;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$scope.promptData = promptData;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let processed = PromptService.processSurveyQuestions({
|
function runWorkflowTemplate(template) {
|
||||||
surveyQuestions: surveyQuestionRes.data.spec
|
InitiatePlaybookRun({ scope: $scope, id: template.id, job_type: 'workflow_job_template' });
|
||||||
});
|
|
||||||
|
|
||||||
$scope.promptData = {
|
|
||||||
launchConf: responses[1].data,
|
|
||||||
launchOptions: responses[0].data,
|
|
||||||
surveyQuestions: processed.surveyQuestions,
|
|
||||||
template: template.id,
|
|
||||||
prompts: PromptService.processPromptValues({
|
|
||||||
launchConf: responses[1].data,
|
|
||||||
launchOptions: responses[0].data
|
|
||||||
}),
|
|
||||||
triggerModalOpen: true
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.promptData = {
|
|
||||||
launchConf: responses[1].data,
|
|
||||||
launchOptions: responses[0].data,
|
|
||||||
template: template.id,
|
|
||||||
prompts: PromptService.processPromptValues({
|
|
||||||
launchConf: responses[1].data,
|
|
||||||
launchOptions: responses[0].data
|
|
||||||
}),
|
|
||||||
triggerModalOpen: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if(template.type && (template.type === 'Workflow Job Template' || template.type === 'workflow_job_template')) {
|
|
||||||
InitiatePlaybookRun({ scope: $scope, id: template.id, job_type: 'workflow_job_template' });
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Something went wrong - Let the user know that we're unable to launch because we don't know
|
|
||||||
// what type of job template this is
|
|
||||||
Alert('Error: Unable to determine template type', 'We were unable to determine this template\'s type while launching.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Alert('Error: Unable to launch template', 'Template parameter is missing');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.launchJob = () => {
|
$scope.launchJob = () => {
|
||||||
|
const jobLaunchData = {
|
||||||
let jobLaunchData = {
|
|
||||||
extra_vars: $scope.promptData.extraVars
|
extra_vars: $scope.promptData.extraVars
|
||||||
};
|
};
|
||||||
|
|
||||||
let jobTemplate = new JobTemplate();
|
if ($scope.promptData.launchConf.ask_tags_on_launch){
|
||||||
|
|
||||||
if($scope.promptData.launchConf.ask_tags_on_launch){
|
|
||||||
jobLaunchData.job_tags = $scope.promptData.prompts.tags.value.map(a => a.value).join();
|
jobLaunchData.job_tags = $scope.promptData.prompts.tags.value.map(a => a.value).join();
|
||||||
}
|
}
|
||||||
if($scope.promptData.launchConf.ask_skip_tags_on_launch){
|
|
||||||
|
if ($scope.promptData.launchConf.ask_skip_tags_on_launch){
|
||||||
jobLaunchData.skip_tags = $scope.promptData.prompts.skipTags.value.map(a => a.value).join();
|
jobLaunchData.skip_tags = $scope.promptData.prompts.skipTags.value.map(a => a.value).join();
|
||||||
}
|
}
|
||||||
if($scope.promptData.launchConf.ask_limit_on_launch && _.has($scope, 'promptData.prompts.limit.value')){
|
|
||||||
|
if ($scope.promptData.launchConf.ask_limit_on_launch && _.has($scope, 'promptData.prompts.limit.value')){
|
||||||
jobLaunchData.limit = $scope.promptData.prompts.limit.value;
|
jobLaunchData.limit = $scope.promptData.prompts.limit.value;
|
||||||
}
|
}
|
||||||
if($scope.promptData.launchConf.ask_job_type_on_launch && _.has($scope, 'promptData.prompts.jobType.value.value')) {
|
|
||||||
|
if ($scope.promptData.launchConf.ask_job_type_on_launch && _.has($scope, 'promptData.prompts.jobType.value.value')) {
|
||||||
jobLaunchData.job_type = $scope.promptData.prompts.jobType.value.value;
|
jobLaunchData.job_type = $scope.promptData.prompts.jobType.value.value;
|
||||||
}
|
}
|
||||||
if($scope.promptData.launchConf.ask_verbosity_on_launch && _.has($scope, 'promptData.prompts.verbosity.value.value')) {
|
|
||||||
|
if ($scope.promptData.launchConf.ask_verbosity_on_launch && _.has($scope, 'promptData.prompts.verbosity.value.value')) {
|
||||||
jobLaunchData.verbosity = $scope.promptData.prompts.verbosity.value.value;
|
jobLaunchData.verbosity = $scope.promptData.prompts.verbosity.value.value;
|
||||||
}
|
}
|
||||||
if($scope.promptData.launchConf.ask_inventory_on_launch && !Empty($scope.promptData.prompts.inventory.value.id)){
|
|
||||||
|
if ($scope.promptData.launchConf.ask_inventory_on_launch && !_.isEmpty($scope.promptData.prompts.inventory.value.id)){
|
||||||
jobLaunchData.inventory_id = $scope.promptData.prompts.inventory.value.id;
|
jobLaunchData.inventory_id = $scope.promptData.prompts.inventory.value.id;
|
||||||
}
|
}
|
||||||
if($scope.promptData.launchConf.ask_credential_on_launch){
|
|
||||||
|
if ($scope.promptData.launchConf.ask_credential_on_launch){
|
||||||
jobLaunchData.credentials = [];
|
jobLaunchData.credentials = [];
|
||||||
$scope.promptData.prompts.credentials.value.forEach((credential) => {
|
$scope.promptData.prompts.credentials.value.forEach((credential) => {
|
||||||
jobLaunchData.credentials.push(credential.id);
|
jobLaunchData.credentials.push(credential.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if($scope.promptData.launchConf.ask_diff_mode_on_launch && _.has($scope, 'promptData.prompts.diffMode.value')) {
|
|
||||||
|
if ($scope.promptData.launchConf.ask_diff_mode_on_launch && _.has($scope, 'promptData.prompts.diffMode.value')) {
|
||||||
jobLaunchData.diff_mode = $scope.promptData.prompts.diffMode.value;
|
jobLaunchData.diff_mode = $scope.promptData.prompts.diffMode.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($scope.promptData.prompts.credentials.passwords) {
|
if ($scope.promptData.prompts.credentials.passwords) {
|
||||||
_.forOwn($scope.promptData.prompts.credentials.passwords, (val, key) => {
|
_.forOwn($scope.promptData.prompts.credentials.passwords, (val, key) => {
|
||||||
if(!jobLaunchData.credential_passwords) {
|
if (!jobLaunchData.credential_passwords) {
|
||||||
jobLaunchData.credential_passwords = {};
|
jobLaunchData.credential_passwords = {};
|
||||||
}
|
}
|
||||||
if(key === "ssh_key_unlock") {
|
if (key === "ssh_key_unlock") {
|
||||||
jobLaunchData.credential_passwords.ssh_key_unlock = val.value;
|
jobLaunchData.credential_passwords.ssh_key_unlock = val.value;
|
||||||
} else if(key !== "vault") {
|
} else if (key !== "vault") {
|
||||||
jobLaunchData.credential_passwords[`${key}_password`] = val.value;
|
jobLaunchData.credential_passwords[`${key}_password`] = val.value;
|
||||||
} else {
|
} else {
|
||||||
_.each(val, (vaultCred) => {
|
_.each(val, (vaultCred) => {
|
||||||
@@ -521,42 +415,30 @@ function TemplatesListController (
|
|||||||
delete jobLaunchData.extra_vars;
|
delete jobLaunchData.extra_vars;
|
||||||
}
|
}
|
||||||
|
|
||||||
jobTemplate.postLaunch({
|
jobTemplate.create().postLaunch({
|
||||||
id: $scope.promptData.template,
|
id: $scope.promptData.template,
|
||||||
launchData: jobLaunchData
|
launchData: jobLaunchData
|
||||||
}).then((launchRes) => {
|
})
|
||||||
|
.then((launchRes) => {
|
||||||
$state.go('jobResult', { id: launchRes.data.job }, { reload: true });
|
$state.go('jobResult', { id: launchRes.data.job }, { reload: true });
|
||||||
}).catch(({data, status}) => {
|
})
|
||||||
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
|
.catch(createErrorHandler('launch job template', 'POST'))
|
||||||
msg: i18n.sprintf(i18n._('Failed to launch job template. POST returned: %d'), $scope.promptData.template, status) });
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
TemplatesListController.$inject = [
|
ListTemplatesController.$inject = [
|
||||||
'$scope',
|
|
||||||
'$rootScope',
|
|
||||||
'Alert',
|
|
||||||
'TemplateList',
|
|
||||||
'Prompt',
|
|
||||||
'ProcessErrors',
|
|
||||||
'GetBasePath',
|
|
||||||
'InitiatePlaybookRun',
|
|
||||||
'Wait',
|
|
||||||
'$state',
|
|
||||||
'$filter',
|
'$filter',
|
||||||
|
'$scope',
|
||||||
|
'$state',
|
||||||
|
'Alert',
|
||||||
'Dataset',
|
'Dataset',
|
||||||
'rbacUiControlService',
|
'InitiatePlaybookRun',
|
||||||
'TemplatesService',
|
'ProcessErrors',
|
||||||
'QuerySet',
|
'Prompt',
|
||||||
'i18n',
|
|
||||||
'JobTemplateModel',
|
|
||||||
'WorkflowJobTemplateModel',
|
|
||||||
'TemplatesStrings',
|
|
||||||
'$q,'
|
|
||||||
'Empty',
|
|
||||||
'i18n',
|
|
||||||
'PromptService',
|
'PromptService',
|
||||||
|
'resolvedModels',
|
||||||
|
'TemplatesStrings',
|
||||||
|
'Wait',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default TemplatesListController;
|
export default ListTemplatesController;
|
||||||
|
|||||||
@@ -2,14 +2,6 @@ import ListController from './list-templates.controller';
|
|||||||
const listTemplate = require('~features/templates/list.view.html');
|
const listTemplate = require('~features/templates/list.view.html');
|
||||||
import { N_ } from '../../src/i18n';
|
import { N_ } from '../../src/i18n';
|
||||||
|
|
||||||
function TemplatesResolve (UnifiedJobTemplate) {
|
|
||||||
return new UnifiedJobTemplate(['get', 'options']);
|
|
||||||
}
|
|
||||||
|
|
||||||
TemplatesResolve.$inject = [
|
|
||||||
'UnifiedJobTemplateModel'
|
|
||||||
];
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'templates',
|
name: 'templates',
|
||||||
route: '/templates',
|
route: '/templates',
|
||||||
@@ -32,10 +24,10 @@ export default {
|
|||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
template_search: {
|
template_search: {
|
||||||
|
dynamic: true,
|
||||||
value: {
|
value: {
|
||||||
type: 'workflow_job_template,job_template'
|
type: 'workflow_job_template,job_template',
|
||||||
},
|
},
|
||||||
dynamic: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
searchPrefix: 'template',
|
searchPrefix: 'template',
|
||||||
@@ -43,16 +35,34 @@ export default {
|
|||||||
'@': {
|
'@': {
|
||||||
controller: ListController,
|
controller: ListController,
|
||||||
templateUrl: listTemplate,
|
templateUrl: listTemplate,
|
||||||
controllerAs: 'vm'
|
controllerAs: 'vm',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
resolvedModels: TemplatesResolve,
|
resolvedModels: [
|
||||||
Dataset: ['TemplateList', 'QuerySet', '$stateParams', 'GetBasePath',
|
'JobTemplateModel',
|
||||||
function(list, qs, $stateParams, GetBasePath) {
|
'WorkflowJobTemplateModel',
|
||||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
(JobTemplate, WorkflowJobTemplate) => {
|
||||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
const models = [
|
||||||
|
new JobTemplate(['options']),
|
||||||
|
new WorkflowJobTemplate(['options']),
|
||||||
|
];
|
||||||
|
return Promise.all(models);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Dataset: [
|
||||||
|
'$stateParams',
|
||||||
|
'Wait',
|
||||||
|
'GetBasePath',
|
||||||
|
'QuerySet',
|
||||||
|
($stateParams, Wait, GetBasePath, qs) => {
|
||||||
|
const searchParam = $stateParams.template_search;
|
||||||
|
const searchPath = GetBasePath('unified_job_templates');
|
||||||
|
|
||||||
|
Wait('start');
|
||||||
|
return qs.search(searchPath, searchParam)
|
||||||
|
.finally(() => Wait('stop'))
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -89,16 +89,16 @@
|
|||||||
</at-row-item>
|
</at-row-item>
|
||||||
<at-row-item
|
<at-row-item
|
||||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_RAN') }}"
|
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_RAN') }}"
|
||||||
value="{{ vm.getRan(template) }}">
|
value="{{ vm.getLastRan(template) }}">
|
||||||
</at-row-item>
|
</at-row-item>
|
||||||
<labels-list class="LabelList" show-delete="false" is-row-item="true">
|
<labels-list class="LabelList" show-delete="false" is-row-item="true">
|
||||||
</labels-list>
|
</labels-list>
|
||||||
</div>
|
</div>
|
||||||
<div class="at-Row-actions">
|
<div class="at-Row-actions">
|
||||||
<at-row-action icon="icon-launch" ng-click="vm.submitJob(template)"
|
<at-row-action icon="icon-launch" ng-click="vm.runTemplate(template)"
|
||||||
ng-show="template.summary_fields.user_capabilities.start">
|
ng-show="template.summary_fields.user_capabilities.start">
|
||||||
</at-row-action>
|
</at-row-action>
|
||||||
<at-row-action icon="fa-calendar" ng-click="vm.scheduleJob(template)"
|
<at-row-action icon="fa-calendar" ng-click="vm.scheduleTemplate(template)"
|
||||||
ng-show="template.summary_fields.user_capabilities.schedule">
|
ng-show="template.summary_fields.user_capabilities.schedule">
|
||||||
</at-row-action>
|
</at-row-action>
|
||||||
<at-row-action icon="fa-copy" ng-click="vm.copyTemplate(template)"
|
<at-row-action icon="fa-copy" ng-click="vm.copyTemplate(template)"
|
||||||
|
|||||||
@@ -55,8 +55,37 @@ function TemplatesStrings (BaseString) {
|
|||||||
VALID_DECIMAL: t.s('Please enter an answer that is a decimal number.'),
|
VALID_DECIMAL: t.s('Please enter an answer that is a decimal number.'),
|
||||||
PLAYBOOK_RUN: t.s('Playbook Run'),
|
PLAYBOOK_RUN: t.s('Playbook Run'),
|
||||||
CHECK: t.s('Check'),
|
CHECK: t.s('Check'),
|
||||||
NO_CREDS_MATCHING_TYPE: t.s('No Credentials Matching This Type Have Been Created')
|
NO_CREDS_MATCHING_TYPE: t.s('No Credentials Matching This Type Have Been Created'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ns.alert = {
|
||||||
|
MISSING_PARAMETER: t.s('Template parameter is missing.'),
|
||||||
|
NO_PERMISSION: t.s('You do not have permission to perform this action.'),
|
||||||
|
UNKNOWN_COPY: t.s('Unable to determine this template\'s type while copying.'),
|
||||||
|
UNKNOWN_DELETE: t.s('Unable to determine this template\'s type while deleting.'),
|
||||||
|
UNKNOWN_EDIT: t.s('Unable to determine this template\'s type while editing.'),
|
||||||
|
UNKNOWN_LAUNCH: t.s('Unable to determine this template\'s type while launching.'),
|
||||||
|
UNKNOWN_SCHEDULE: t.s('Unable to determine this template\'s type while scheduling.'),
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.actions = {
|
||||||
|
COPY_WORKFLOW: t.s('Copy Workflow')
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.error = {
|
||||||
|
HEADER: this.error.HEADER,
|
||||||
|
CALL: this.error.CALL,
|
||||||
|
EDIT: t.s('Unable to edit template.'),
|
||||||
|
DELETE: t.s('Unable to delete template.'),
|
||||||
|
LAUNCH: t.s('Unable to launch template.'),
|
||||||
|
UNKNOWN: t.s('Unable to determine template type.'),
|
||||||
|
SCHEDULE: t.s('Unable to schedule job.'),
|
||||||
|
COPY: t.s('Unable to copy template.'),
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.warnings = {
|
||||||
|
WORKFLOW_RESTRICTED_COPY: t.s('You do not have access to all resources used by this workflow. Resources that you don\'t have access to will not be copied and will result in an incomplete workflow.')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TemplatesStrings.$inject = ['BaseStringService'];
|
TemplatesStrings.$inject = ['BaseStringService'];
|
||||||
|
|||||||
@@ -67,15 +67,18 @@ function BaseStringService (namespace) {
|
|||||||
this.OFF = t.s('OFF');
|
this.OFF = t.s('OFF');
|
||||||
this.YAML = t.s('YAML');
|
this.YAML = t.s('YAML');
|
||||||
this.JSON = t.s('JSON');
|
this.JSON = t.s('JSON');
|
||||||
|
this.DELETE = t.s('DELETE');
|
||||||
|
this.COPY = t.s('COPY');
|
||||||
|
|
||||||
this.deleteResource = {
|
this.deleteResource = {
|
||||||
HEADER: t.s('Delete'),
|
HEADER: t.s('Delete'),
|
||||||
USED_BY: resourceType => t.s('The {{ resourceType }} is currently being used by other resources.', { resourceType }),
|
USED_BY: resourceType => t.s('The {{ resourceType }} is currently being used by other resources.', { resourceType }),
|
||||||
CONFIRM: resourceType => t.s('Are you sure you want to delete this {{ resourceType }}?', { resourceType })
|
CONFIRM: resourceType => t.s('Are you sure you want to delete this {{ resourceType }}?', { resourceType })
|
||||||
};
|
};
|
||||||
|
|
||||||
this.error = {
|
this.error = {
|
||||||
HEADER: t.s('Error!'),
|
HEADER: t.s('Error!'),
|
||||||
CALL: ({ path, status }) => t.s('Call to {{ path }} failed. DELETE returned status: {{ status }}.', { path, status })
|
CALL: ({ path, action, status }) => t.s('Call to {{ path }} failed. {{ action }} returned status: {{ status }}.', { path, action, status }),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ALERT = ({ header, body }) => t.s('{{ header }} {{ body }}', { header, body });
|
this.ALERT = ({ header, body }) => t.s('{{ header }} {{ body }}', { header, body });
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2016 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
export default
|
|
||||||
['$rootScope', 'Rest', 'ProcessErrors', 'GetBasePath', 'moment',
|
|
||||||
function($rootScope, Rest, ProcessErrors, GetBasePath, moment){
|
|
||||||
return {
|
|
||||||
get: function(id){
|
|
||||||
var defaultUrl = GetBasePath('job_templates') + '?id=' + id;
|
|
||||||
Rest.setUrl(defaultUrl);
|
|
||||||
return Rest.get()
|
|
||||||
.then(response => response)
|
|
||||||
.catch((error) => {
|
|
||||||
ProcessErrors($rootScope, error.response, error.status, null, {hdr: 'Error!',
|
|
||||||
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getSurvey: function(endpoint){
|
|
||||||
Rest.setUrl(endpoint);
|
|
||||||
return Rest.get();
|
|
||||||
},
|
|
||||||
copySurvey: function(source, target){
|
|
||||||
return this.getSurvey(source.related.survey_spec).then( (response) => {
|
|
||||||
Rest.setUrl(target.related.survey_spec);
|
|
||||||
return Rest.post(response.data);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
set: function(results){
|
|
||||||
var defaultUrl = GetBasePath('job_templates');
|
|
||||||
var self = this;
|
|
||||||
Rest.setUrl(defaultUrl);
|
|
||||||
var name = this.buildName(results[0].name);
|
|
||||||
results[0].name = name + ' @ ' + moment().format('h:mm:ss a'); // 2:49:11 pm
|
|
||||||
return Rest.post(results[0])
|
|
||||||
.then((response) => {
|
|
||||||
// also copy any associated survey_spec
|
|
||||||
if (results[0].summary_fields.survey){
|
|
||||||
return self.copySurvey(results[0], response.data).then( () => response.data);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(({res, status}) => {
|
|
||||||
ProcessErrors($rootScope, res, status, null, {hdr: 'Error!',
|
|
||||||
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
buildName: function(name){
|
|
||||||
var result = name.split('@')[0];
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
getWorkflowCopy: function(id) {
|
|
||||||
let url = GetBasePath('workflow_job_templates');
|
|
||||||
|
|
||||||
url = url + id + '/copy';
|
|
||||||
|
|
||||||
Rest.setUrl(url);
|
|
||||||
return Rest.get();
|
|
||||||
},
|
|
||||||
getWorkflowCopyName: function(baseName) {
|
|
||||||
return `${baseName}@${moment().format('h:mm:ss a')}`;
|
|
||||||
},
|
|
||||||
copyWorkflow: function(id, name) {
|
|
||||||
let url = GetBasePath('workflow_job_templates');
|
|
||||||
|
|
||||||
url = url + id + '/copy';
|
|
||||||
|
|
||||||
Rest.setUrl(url);
|
|
||||||
return Rest.post({ name });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -15,7 +15,6 @@ import workflowChart from './workflows/workflow-chart/main';
|
|||||||
import workflowMaker from './workflows/workflow-maker/main';
|
import workflowMaker from './workflows/workflow-maker/main';
|
||||||
import workflowControls from './workflows/workflow-controls/main';
|
import workflowControls from './workflows/workflow-controls/main';
|
||||||
import workflowService from './workflows/workflow.service';
|
import workflowService from './workflows/workflow.service';
|
||||||
import templateCopyService from './copy-template/template-copy.service';
|
|
||||||
import WorkflowForm from './workflows.form';
|
import WorkflowForm from './workflows.form';
|
||||||
import CompletedJobsList from './completed-jobs.list';
|
import CompletedJobsList from './completed-jobs.list';
|
||||||
import InventorySourcesList from './inventory-sources.list';
|
import InventorySourcesList from './inventory-sources.list';
|
||||||
@@ -29,7 +28,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
|
|||||||
])
|
])
|
||||||
.service('TemplatesService', templatesService)
|
.service('TemplatesService', templatesService)
|
||||||
.service('WorkflowService', workflowService)
|
.service('WorkflowService', workflowService)
|
||||||
.service('TemplateCopyService', templateCopyService)
|
|
||||||
.factory('WorkflowForm', WorkflowForm)
|
.factory('WorkflowForm', WorkflowForm)
|
||||||
.factory('CompletedJobsList', CompletedJobsList)
|
.factory('CompletedJobsList', CompletedJobsList)
|
||||||
// TODO: currently being kept arround for rbac selection, templates within projects and orgs, etc.
|
// TODO: currently being kept arround for rbac selection, templates within projects and orgs, etc.
|
||||||
|
|||||||
Reference in New Issue
Block a user