diff --git a/awx/ui/client/features/templates/list-templates.controller.js b/awx/ui/client/features/templates/list-templates.controller.js
index 9e5d2b18ef..927e807656 100644
--- a/awx/ui/client/features/templates/list-templates.controller.js
+++ b/awx/ui/client/features/templates/list-templates.controller.js
@@ -1,148 +1,75 @@
/** ***********************************************
- * Copyright (c) 2017 Ansible, Inc.
+ * Copyright (c) 2018 Ansible, Inc.
*
* 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 WORKFLOW_TEMPLATE_ALIASES = ['workflow_job_template', 'Workflow Job Template'];
-const isJobTemplate = obj => _.includes(JOB_TEMPLATE_ALIASES, _.get(obj, 'type'));
-const isWorkflowTemplate = obj => _.includes(WORKFLOW_TEMPLATE_ALIASES, _.get(obj, 'type'));
+const isJobTemplate = ({ type }) => JOB_TEMPLATE_ALIASES.indexOf(type) > -1;
+const isWorkflowTemplate = ({ type }) => WORKFLOW_TEMPLATE_ALIASES.indexOf(type) > -1;
+const mapChoices = choices => Object.assign(...choices.map(([k, v]) => ({[k]: v})));
-function TemplatesListController (
- $scope,
- $rootScope,
- Alert,
- TemplateList,
- Prompt,
- ProcessErrors,
- GetBasePath,
- InitiatePlaybookRun,
- Wait,
- $state,
+function ListTemplatesController(
$filter,
+ $scope,
+ $state,
+ Alert,
Dataset,
- rbacUiControlService,
- TemplatesService,
- qs,
- i18n,
- JobTemplate,
- WorkflowJobTemplate,
- TemplatesStrings,
- $q,
- Empty,
- i18n,
+ InitiatePlaybookRun,
+ ProcessErrors,
+ Prompt,
PromptService,
+ resolvedModels,
+ strings,
+ Wait,
) {
- const jobTemplate = new JobTemplate();
- const list = TemplateList;
+ const vm = this || {};
+ const [jobTemplate, workflowTemplate] = resolvedModels;
- init();
+ const choices = workflowTemplate.options('actions.GET.type.choices')
+ .concat(jobTemplate.options('actions.GET.type.choices'));
- function init () {
- $scope.canAdd = false;
+ vm.strings = strings;
+ 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 = params.canAdd;
- });
+ $scope.canAddJobTemplate = jobTemplate.options('actions.POST')
+ $scope.canAddWorkflowJobTemplate = workflowTemplate.options('actions.POST')
+ $scope.canAdd = ($scope.canAddJobTemplate || $scope.canAddWorkflowJobTemplate);
- rbacUiControlService.canAdd('workflow_job_templates').then(params => {
- $scope.canAddWorkflowJobTemplate = params.canAdd;
- });
+ // smart-search
+ const name = 'templates';
+ const iterator = 'template';
+ const key = 'template_dataset';
- // search init
- $scope.list = list;
- $scope[`${list.iterator}_dataset`] = Dataset.data;
- $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
- $scope.options = {};
-
- $rootScope.flashMessage = null;
- }
-
- $scope.$on(`${list.iterator}_options`, (event, data) => {
- $scope.options = data.data.actions.GET;
- optionsRequestDataProcessing();
+ $scope.list = { iterator, name };
+ $scope.collection = { iterator, basePath: 'unified_job_templates' };
+ $scope[key] = Dataset.data;
+ $scope[name] = Dataset.data.results;
+ $scope.$on('updateDataset', (e, dataset) => {
+ $scope[key] = dataset;
+ $scope[name] = dataset.results;
});
- $scope.$watchCollection('templates', () => {
- 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 => {
+ vm.runTemplate = template => {
if (!template) {
- Alert(ERROR_EDIT, ALERT_MISSING);
+ Alert(strings.get('error.LAUNCH'), strings.get('alert.MISSING_PARAMETER'));
return;
}
if (isJobTemplate(template)) {
- $state.transitionTo('templates.editJobTemplate', { job_template_id: template.id });
+ runJobTemplate(template);
} else if (isWorkflowTemplate(template)) {
- $state.transitionTo('templates.editWorkflowJobTemplate', { workflow_job_template_id: template.id });
+ runWorkflowTemplate(template);
} 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) {
- Alert(ERROR_LAUNCH, ALERT_MISSING);
- 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);
+ Alert(strings.get('error.SCHEDULE'), strings.get('alert.MISSING_PARAMETER'));
return;
}
@@ -151,362 +78,329 @@ function TemplatesListController (
} else if (isWorkflowTemplate(template)) {
$state.go('workflowJobTemplateSchedules', { id: template.id });
} 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) {
- Alert(ERROR_DELETE, ALERT_MISSING);
+ Alert(strings.get('error.DELETE'), strings.get('alert.MISSING_PARAMETER'));
return;
}
if (isWorkflowTemplate(template)) {
- const body = TemplatesStrings.get('deleteResource.CONFIRM', 'workflow job template');
- $scope.displayTemplateDeletePrompt(template, body);
+ displayWorkflowTemplateDeletePrompt(template);
} else if (isJobTemplate(template)) {
- jobTemplate.getDependentResourceCounts(template.id)
- .then(counts => {
- const body = buildTemplateDeletePromptHTML(counts);
- $scope.displayTemplateDeletePrompt(template, body);
- });
+ jobTemplate.getDependentResourceCounts(template.id)
+ .then(counts => displayJobTemplateDeletePrompt(template, counts));
} 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) {
- Alert(ERROR_TEMPLATE_COPY, ALERT_MISSING);
+ Alert(strings.get('error.COPY'), strings.get('alert.MISSING_PARAMETER'));
return;
}
if (isJobTemplate(template)) {
- $scope.copyJobTemplate(template);
+ copyJobTemplate(template);
} else if (isWorkflowTemplate(template)) {
- $scope.copyWorkflowJobTemplate(template);
+ copyWorkflowTemplate(template);
} 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 ${$filter('sanitize')(username)}`;
+ }
+
+ 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 ${$filter('sanitize')(username)}`;
+ //}
+
+ 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');
- new JobTemplate('get', template.id)
+ jobTemplate
+ .create('get', template.id)
.then(model => model.copy())
.then(({ id }) => {
const params = { job_template_id: id };
$state.go('templates.editJobTemplate', params, { reload: true });
})
- .catch(({ data, status }) => {
- const params = { hdr: 'Error!', msg: `Call to copy failed. Return status: ${status}` };
- ProcessErrors($scope, data, status, null, params);
- })
+ .catch(createErrorHandler('copy job template', 'POST'))
.finally(() => Wait('stop'));
};
- $scope.copyWorkflowJobTemplate = template => {
+ function copyWorkflowTemplate(template) {
Wait('start');
- new WorkflowJobTemplate('get', template.id)
- .then(model => model.extend('GET', 'copy'))
+ workflowTemplate
+ .create('get', template.id)
+ .then(model => model.extend('get', 'copy'))
.then(model => {
const action = () => {
+ Wait('start');
model.copy()
.then(({ id }) => {
const params = { workflow_job_template_id: id };
$state.go('templates.editWorkflowJobTemplate', params, { reload: true });
})
- .catch(({ data, status }) => {
- const params = { hdr: 'Error!', msg: `Call to copy failed. Return status: ${status}` };
- ProcessErrors($scope, data, status, null, params);
- });
+ .catch(createErrorHandler('copy workflow', 'POST'))
+ .finally(() => Wait('stop'));
};
+
if (model.get('related.copy.can_copy_without_user_input')) {
action();
} else if (model.get('related.copy.can_copy')) {
Prompt({
- hdr: 'Copy Workflow',
action,
- actionText: 'COPY',
+ actionText: strings.get('COPY'),
+ body: buildWorkflowCopyPromptHTML(model.get('related.copy')),
class: 'Modal-primaryButton',
- body: buildWorkflowCopyPromptHTML(model.get('related.copy'))
+ hdr: strings.get('actions.COPY_WORKFLOW'),
});
} else {
- Alert(ERROR_WORKFLOW_COPY, ALERT_NO_PERMISSION);
+ Alert(strings.get('error.COPY'), strings.get('alert.NO_PERMISSION'));
}
})
- .catch(({ data, status }) => {
- const params = { hdr: 'Error!', msg: `Call to copy failed. Return status: ${status}` };
- ProcessErrors($scope, data, status, null, params);
- })
+ .catch(createErrorHandler('copy workflow', 'GET'))
.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 = `
-
- ${TemplatesStrings.get('deleteResource.CONFIRM', 'job template')}
-
`;
-
- dependentResourceCounts.forEach(countObj => {
- if (countObj.count && countObj.count > 0) {
- invalidateRelatedLines.push(`
-
- ${countObj.label}
-
-
- ${countObj.count}
-
-
`);
- }
- });
-
- if (invalidateRelatedLines && invalidateRelatedLines.length > 0) {
- bodyHTML = `
-
- ${TemplatesStrings.get('deleteResource.USED_BY', 'job template')}
- ${TemplatesStrings.get('deleteResource.CONFIRM', 'job template')}
-
`;
- invalidateRelatedLines.forEach(invalidateRelatedLine => {
- bodyHTML += invalidateRelatedLine;
- });
- }
-
- return bodyHTML;
}
- function buildWorkflowCopyPromptHTML (data) {
- const {
- credentials_unable_to_copy,
- inventories_unable_to_copy,
- job_templates_unable_to_copy
- } = data;
+ function handleSuccessfulDelete(template) {
+ const { page } = _.get($state.params, 'template_search');
+ let reloadListStateParams = null;
- let itemsHTML = '';
-
- if (job_templates_unable_to_copy.length > 0) {
- itemsHTML += 'Unified Job Templates that cannot be copied
';
- _.forOwn(job_templates_unable_to_copy, ujt => {
- if (ujt) {
- itemsHTML += `- '${ujt}
`;
- }
- });
- itemsHTML += '
';
+ if ($scope.templates.length === 1 && !_.isEmpty(page) && page !== '1') {
+ reloadListStateParams = _.cloneDeep($state.params);
+ const pageNumber = (parseInt(reloadListStateParams.template_search.page, 0) - 1);
+ reloadListStateParams.template_search.page = pageNumber.toString();
}
- if (inventories_unable_to_copy.length > 0) {
- itemsHTML += 'Node prompted inventories that cannot be copied
';
- _.forOwn(inventories_unable_to_copy, inv => {
- if (inv) {
- itemsHTML += `- '${inv}
`;
- }
- });
- itemsHTML += '
';
+ if (parseInt($state.params.job_template_id, 0) === template.id) {
+ $state.go('templates', reloadListStateParams, { reload: true });
+ } else if (parseInt($state.params.workflow_job_template_id, 0) === template.id) {
+ $state.go('templates', reloadListStateParams, { reload: true });
+ } else {
+ $state.go('.', reloadListStateParams, { reload: true });
}
+ }
- if (credentials_unable_to_copy.length > 0) {
- itemsHTML += 'Node prompted credentials that cannot be copied
';
- _.forOwn(credentials_unable_to_copy, cred => {
- if (cred) {
- itemsHTML += `- '${cred}
`;
- }
- });
- itemsHTML += '
';
- }
+ function displayJobTemplateDeletePrompt(template, counts) {
+ Prompt({
+ action() {
+ $('#prompt-modal').modal('hide');
+ Wait('start');
+ jobTemplate
+ .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: 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 => `${count}`;
+ const buildLabel = label => `
+ ${$filter('sanitize')(label)}`;
+ const buildCountLabel = ({ count, label }) => `
+ ${buildLabel(label)}${buildCount(count)}
`;
+
+ 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 = `
- 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.
+ ${strings.get('warnings.WORKFLOW_RESTRICTED_COPY')}
- ${itemsHTML}
+ ${templates.length ? `
Unified Job Templates
` : ''}
+ ${templates.map(item => `- ${item}
`).join('')}
+ ${templates.length ? `
` : ''}
+
+
+ ${credentials.length ? `
Credentials
` : ''}
+ ${credentials.map(item => `- ${item}
`).join('')}
+ ${credentials.length ? `
` : ''}
+
+
+ ${inventories.length ? `
Inventories
` : ''}
+ ${inventories.map(item => `- ${item}
`).join('')}
+ ${inventories.length ? `
` : ''}
`;
- return bodyHTML;
+ return html;
}
- function submitJobTemplate(template) {
- if(template) {
- if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) {
- let jobTemplate = new JobTemplate();
+ function runJobTemplate(template) {
+ const selectedJobTemplate = jobTemplate.create();
+ const preLaunchPromises = [
+ selectedJobTemplate.getLaunch(template.id),
+ selectedJobTemplate.optionsLaunch(template.id),
+ ];
- $q.all([jobTemplate.optionsLaunch(template.id), jobTemplate.getLaunch(template.id)])
- .then((responses) => {
- if(jobTemplate.canLaunchWithoutPrompt()) {
- jobTemplate.postLaunch({id: template.id})
- .then((launchRes) => {
- $state.go('jobResult', { id: launchRes.data.job }, { reload: true });
- });
- } else {
+ Promise.all(preLaunchPromises)
+ .then(([launchData, launchOptions]) => {
+ if (selectedJobTemplate.canLaunchWithoutPrompt()) {
+ return selectedJobTemplate
+ .postLaunch({ id: template.id })
+ .then(({ data }) => {
+ $state.go('jobResult', { id: data.job }, { reload: true })
+ });
+ }
- 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
- jobTemplate.getSurveyQuestions(template.id)
- .then((surveyQuestionRes) => {
+ if (launchData.data.survey_enabled) {
+ selectedJobTemplate.getSurveyQuestions(template.id)
+ .then(({ data }) => {
+ const processed = PromptService.processSurveyQuestions({ surveyQuestions: data.spec });
+ promptData.surveyQuestions = processed.surveyQuestions;
+ $scope.promptData = promptData;
+ });
+ } else {
+ $scope.promptData = promptData;
+ }
+ });
+ }
- let processed = PromptService.processSurveyQuestions({
- surveyQuestions: surveyQuestionRes.data.spec
- });
-
- $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');
- }
+ function runWorkflowTemplate(template) {
+ InitiatePlaybookRun({ scope: $scope, id: template.id, job_type: 'workflow_job_template' });
}
$scope.launchJob = () => {
-
- let jobLaunchData = {
+ const jobLaunchData = {
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();
}
- 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();
}
- 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;
}
- 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;
}
- 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;
}
- 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;
}
- if($scope.promptData.launchConf.ask_credential_on_launch){
+
+ if ($scope.promptData.launchConf.ask_credential_on_launch){
jobLaunchData.credentials = [];
$scope.promptData.prompts.credentials.value.forEach((credential) => {
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;
}
- if($scope.promptData.prompts.credentials.passwords) {
+ if ($scope.promptData.prompts.credentials.passwords) {
_.forOwn($scope.promptData.prompts.credentials.passwords, (val, key) => {
- if(!jobLaunchData.credential_passwords) {
+ if (!jobLaunchData.credential_passwords) {
jobLaunchData.credential_passwords = {};
}
- if(key === "ssh_key_unlock") {
+ if (key === "ssh_key_unlock") {
jobLaunchData.credential_passwords.ssh_key_unlock = val.value;
- } else if(key !== "vault") {
+ } else if (key !== "vault") {
jobLaunchData.credential_passwords[`${key}_password`] = val.value;
} else {
_.each(val, (vaultCred) => {
@@ -521,42 +415,30 @@ function TemplatesListController (
delete jobLaunchData.extra_vars;
}
- jobTemplate.postLaunch({
+ jobTemplate.create().postLaunch({
id: $scope.promptData.template,
launchData: jobLaunchData
- }).then((launchRes) => {
+ })
+ .then((launchRes) => {
$state.go('jobResult', { id: launchRes.data.job }, { reload: true });
- }).catch(({data, status}) => {
- ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
- msg: i18n.sprintf(i18n._('Failed to launch job template. POST returned: %d'), $scope.promptData.template, status) });
- });
+ })
+ .catch(createErrorHandler('launch job template', 'POST'))
};
}
-TemplatesListController.$inject = [
- '$scope',
- '$rootScope',
- 'Alert',
- 'TemplateList',
- 'Prompt',
- 'ProcessErrors',
- 'GetBasePath',
- 'InitiatePlaybookRun',
- 'Wait',
- '$state',
+ListTemplatesController.$inject = [
'$filter',
+ '$scope',
+ '$state',
+ 'Alert',
'Dataset',
- 'rbacUiControlService',
- 'TemplatesService',
- 'QuerySet',
- 'i18n',
- 'JobTemplateModel',
- 'WorkflowJobTemplateModel',
- 'TemplatesStrings',
- '$q,'
- 'Empty',
- 'i18n',
+ 'InitiatePlaybookRun',
+ 'ProcessErrors',
+ 'Prompt',
'PromptService',
+ 'resolvedModels',
+ 'TemplatesStrings',
+ 'Wait',
];
-export default TemplatesListController;
\ No newline at end of file
+export default ListTemplatesController;
diff --git a/awx/ui/client/features/templates/list.route.js b/awx/ui/client/features/templates/list.route.js
index 384164b421..2750a0e8a7 100644
--- a/awx/ui/client/features/templates/list.route.js
+++ b/awx/ui/client/features/templates/list.route.js
@@ -2,14 +2,6 @@ import ListController from './list-templates.controller';
const listTemplate = require('~features/templates/list.view.html');
import { N_ } from '../../src/i18n';
-function TemplatesResolve (UnifiedJobTemplate) {
- return new UnifiedJobTemplate(['get', 'options']);
-}
-
-TemplatesResolve.$inject = [
- 'UnifiedJobTemplateModel'
-];
-
export default {
name: 'templates',
route: '/templates',
@@ -32,10 +24,10 @@ export default {
},
params: {
template_search: {
+ dynamic: true,
value: {
- type: 'workflow_job_template,job_template'
- },
- dynamic: true
+ type: 'workflow_job_template,job_template',
+ },
}
},
searchPrefix: 'template',
@@ -43,16 +35,34 @@ export default {
'@': {
controller: ListController,
templateUrl: listTemplate,
- controllerAs: 'vm'
+ controllerAs: 'vm',
}
},
resolve: {
- resolvedModels: TemplatesResolve,
- Dataset: ['TemplateList', 'QuerySet', '$stateParams', 'GetBasePath',
- function(list, qs, $stateParams, GetBasePath) {
- let path = GetBasePath(list.basePath) || GetBasePath(list.name);
- return qs.search(path, $stateParams[`${list.iterator}_search`]);
+ resolvedModels: [
+ 'JobTemplateModel',
+ 'WorkflowJobTemplateModel',
+ (JobTemplate, WorkflowJobTemplate) => {
+ 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'))
}
- ]
+ ],
}
};
diff --git a/awx/ui/client/features/templates/list.view.html b/awx/ui/client/features/templates/list.view.html
index d22d64cfb4..7759287085 100644
--- a/awx/ui/client/features/templates/list.view.html
+++ b/awx/ui/client/features/templates/list.view.html
@@ -89,16 +89,16 @@
+ value="{{ vm.getLastRan(template) }}">
-
-
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 })
};
+
this.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 });
diff --git a/awx/ui/client/src/templates/copy-template/template-copy.service.js b/awx/ui/client/src/templates/copy-template/template-copy.service.js
deleted file mode 100644
index e55e0d2da3..0000000000
--- a/awx/ui/client/src/templates/copy-template/template-copy.service.js
+++ /dev/null
@@ -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 });
- }
- };
- }
- ];
diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js
index abc320451a..5d1eee77d8 100644
--- a/awx/ui/client/src/templates/main.js
+++ b/awx/ui/client/src/templates/main.js
@@ -15,7 +15,6 @@ import workflowChart from './workflows/workflow-chart/main';
import workflowMaker from './workflows/workflow-maker/main';
import workflowControls from './workflows/workflow-controls/main';
import workflowService from './workflows/workflow.service';
-import templateCopyService from './copy-template/template-copy.service';
import WorkflowForm from './workflows.form';
import CompletedJobsList from './completed-jobs.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('WorkflowService', workflowService)
- .service('TemplateCopyService', templateCopyService)
.factory('WorkflowForm', WorkflowForm)
.factory('CompletedJobsList', CompletedJobsList)
// TODO: currently being kept arround for rbac selection, templates within projects and orgs, etc.