mirror of
https://github.com/ansible/awx.git
synced 2026-01-13 02:50:02 -03:30
510 lines
18 KiB
JavaScript
510 lines
18 KiB
JavaScript
/** ***********************************************
|
|
* Copyright (c) 2018 Ansible, Inc.
|
|
*
|
|
* All Rights Reserved
|
|
************************************************ */
|
|
|
|
const mapChoices = choices => Object.assign(...choices.map(([k, v]) => ({ [k]: v.toUpperCase() })));
|
|
|
|
function projectsListController (
|
|
$filter, $scope, $rootScope, $state, $log, Dataset, Alert, Rest,
|
|
ProcessErrors, resolvedModels, strings, Wait, ngToast,
|
|
Prompt, GetBasePath, qs, ProjectUpdate,
|
|
) {
|
|
const vm = this || {};
|
|
const [ProjectModel] = resolvedModels;
|
|
let paginateQuerySet = null;
|
|
$scope.canAdd = ProjectModel.options('actions.POST');
|
|
|
|
vm.strings = strings;
|
|
vm.scm_choices = ProjectModel.options('actions.GET.scm_type.choices');
|
|
vm.projectTypes = mapChoices(vm.scm_choices);
|
|
|
|
// smart-search
|
|
vm.list = {
|
|
iterator: 'project',
|
|
name: 'projects',
|
|
basePath: 'projects',
|
|
};
|
|
vm.dataset = Dataset.data;
|
|
vm.projects = Dataset.data.results;
|
|
|
|
$scope.$watch('vm.dataset.count', () => {
|
|
$scope.$emit('updateCount', vm.dataset.count, 'projects');
|
|
});
|
|
// build tooltips
|
|
_.forEach(vm.projects, buildTooltips);
|
|
$rootScope.flashMessage = null;
|
|
|
|
// when a project is added/deleted, rebuild tooltips
|
|
$scope.$watchCollection('vm.projects', () => {
|
|
_.forEach(vm.projects, buildTooltips);
|
|
});
|
|
// show active item in the list
|
|
$scope.$watch('$state.params', () => {
|
|
const projectId = _.get($state.params, 'project_id');
|
|
if ((projectId)) {
|
|
vm.activeId = parseInt($state.params.project_id, 10);
|
|
} else {
|
|
vm.activeId = '';
|
|
}
|
|
setToolbarSort();
|
|
}, true);
|
|
|
|
function setToolbarSort () {
|
|
const orderByValue = _.get($state.params, 'project_search.order_by');
|
|
const sortValue = _.find(vm.toolbarSortOptions, (option) => option.value === orderByValue);
|
|
if (sortValue) {
|
|
vm.toolbarSortValue = sortValue;
|
|
} else {
|
|
vm.toolbarSortValue = toolbarSortDefault;
|
|
}
|
|
}
|
|
|
|
const toolbarSortDefault = {
|
|
label: `${strings.get('sort.NAME_ASCENDING')}`,
|
|
value: 'name'
|
|
};
|
|
|
|
vm.toolbarSortOptions = [
|
|
toolbarSortDefault,
|
|
{
|
|
label: `${strings.get('sort.NAME_DESCENDING')}`,
|
|
value: '-name'
|
|
}
|
|
];
|
|
|
|
vm.toolbarSortValue = toolbarSortDefault;
|
|
|
|
// Temporary hack to retrieve $scope.querySet from the paginate directive.
|
|
// Remove this event listener once the page and page_size params
|
|
// are represented in the url.
|
|
$scope.$on('updateDataset', (event, dataset, queryset) => {
|
|
paginateQuerySet = queryset;
|
|
});
|
|
|
|
vm.onToolbarSort = (sort) => {
|
|
vm.toolbarSortValue = sort;
|
|
|
|
const queryParams = Object.assign(
|
|
{},
|
|
$state.params.project_search,
|
|
paginateQuerySet,
|
|
{ order_by: sort.value }
|
|
);
|
|
|
|
// Update URL with params
|
|
$state.go('.', {
|
|
project_search: queryParams
|
|
}, { notify: false, location: 'replace' });
|
|
|
|
qs.search(GetBasePath(vm.list.basePath), queryParams)
|
|
.then(({ data }) => {
|
|
vm.dataset = data;
|
|
vm.projects = vm.dataset.results;
|
|
});
|
|
};
|
|
|
|
$scope.$on('ws-jobs', (e, data) => {
|
|
$log.debug(data);
|
|
if (vm.projects) {
|
|
// Assuming we have a list of projects available
|
|
const project = vm.projects.find((p) => p.id === data.project_id);
|
|
if (project) {
|
|
// And we found the affected project
|
|
$log.debug(`Received event for project: ${project.name}`);
|
|
$log.debug(`Status changed to: ${data.status}`);
|
|
if (data.status === 'successful' || data.status === 'failed' || data.status === 'canceled') {
|
|
reloadList();
|
|
} else {
|
|
project.scm_update_tooltip = vm.strings.get('update.UPDATE_RUNNING');
|
|
}
|
|
project.status = data.status;
|
|
buildTooltips(project);
|
|
}
|
|
}
|
|
});
|
|
|
|
if ($scope.removeGoTojobResults) {
|
|
$scope.removeGoTojobResults();
|
|
}
|
|
|
|
$scope.removeGoTojobResults = $scope.$on('GoTojobResults', (e, data) => {
|
|
if (data.summary_fields.current_update || data.summary_fields.last_update) {
|
|
Wait('start');
|
|
// Grab the id from summary_fields
|
|
const updateJobid = (data.summary_fields.current_update) ?
|
|
data.summary_fields.current_update.id : data.summary_fields.last_update.id;
|
|
|
|
$state.go('output', { id: updateJobid, type: 'project' }, { reload: true });
|
|
} else {
|
|
Alert(vm.strings.get('alert.NO_UPDATE'), vm.strings.get('update.NO_UPDATE_INFO'), 'alert-info');
|
|
}
|
|
});
|
|
|
|
if ($scope.removeCancelUpdate) {
|
|
$scope.removeCancelUpdate();
|
|
}
|
|
|
|
$scope.removeCancelUpdate = $scope.$on('Cancel_Update', (e, url) => {
|
|
// Cancel the project update process
|
|
Rest.setUrl(url);
|
|
Rest.post()
|
|
.then(() => {
|
|
Alert(vm.strings.get('alert.UPDATE_CANCEL'), vm.strings.get('update.CANCEL_UPDATE_REQUEST'), 'alert-info');
|
|
})
|
|
.catch(createErrorHandler(url, 'POST'));
|
|
});
|
|
|
|
if ($scope.removeCheckCancel) {
|
|
$scope.removeCheckCancel();
|
|
}
|
|
|
|
$scope.removeCheckCancel = $scope.$on('Check_Cancel', (e, projectData) => {
|
|
// Check that we 'can' cancel the update
|
|
const url = projectData.related.cancel;
|
|
Rest.setUrl(url);
|
|
Rest.get()
|
|
.then(({ data }) => {
|
|
if (data.can_cancel) {
|
|
$scope.$emit('Cancel_Update', url);
|
|
} else {
|
|
Alert(vm.strings.get('alert.CANCEL_NOT_ALLOWED'), vm.strings.get('update.NO_ACCESS_OR_COMPLETED_UPDATE'), 'alert-info', null, null, null, null, true);
|
|
}
|
|
})
|
|
.catch(createErrorHandler(url, 'GET'));
|
|
});
|
|
|
|
vm.showSCMStatus = (id) => {
|
|
// Refresh the project list
|
|
const project = vm.projects.find((p) => p.id === id);
|
|
|
|
if ((!project.scm_type) || project.scm_type === 'Manual') {
|
|
Alert(vm.strings.get('alert.NO_SCM_CONFIG'), vm.strings.get('update.NO_PROJ_SCM_CONFIG'), 'alert-info');
|
|
} else {
|
|
// Refresh what we have in memory
|
|
// to insure we're accessing the most recent status record
|
|
Rest.setUrl(project.url);
|
|
Rest.get()
|
|
.then(({ data }) => {
|
|
$scope.$emit('GoTojobResults', data);
|
|
})
|
|
.catch(createErrorHandler(project.url, 'GET'));
|
|
}
|
|
};
|
|
|
|
vm.getLastModified = project => {
|
|
const modified = _.get(project, 'modified');
|
|
|
|
if (!modified) {
|
|
return undefined;
|
|
}
|
|
|
|
const html = $filter('longDate')(modified);
|
|
|
|
// NEED api to add field project.summary_fields.modified_by
|
|
|
|
// const { username, id } = _.get(project, 'summary_fields.modified_by', {});
|
|
|
|
// if (username && id) {
|
|
// html += ` by <a href="/#/users/${id}">${$filter('sanitize')(username)}</a>`;
|
|
// }
|
|
|
|
return html;
|
|
};
|
|
|
|
vm.getLastUsed = project => {
|
|
const modified = _.get(project, 'last_job_run');
|
|
|
|
if (!modified) {
|
|
return undefined;
|
|
}
|
|
|
|
const html = $filter('longDate')(modified);
|
|
|
|
// NEED api to add last_job user information such as launch_by
|
|
|
|
// const { id } = _.get(project, 'summary_fields.last_job', {});
|
|
// if (id) {
|
|
// html += ` by <a href="/#/jobs/project/${id}">
|
|
// ${$filter('sanitize')('placehoder')}</a>`;
|
|
// }
|
|
return html;
|
|
};
|
|
|
|
vm.copyProject = project => {
|
|
Wait('start');
|
|
ProjectModel
|
|
.create('get', project.id)
|
|
.then(model => model.copy())
|
|
.then((copiedProj) => {
|
|
ngToast.success({
|
|
content: `
|
|
<div class="Toast-wrapper">
|
|
<div class="Toast-icon">
|
|
<i class="fa fa-check-circle Toast-successIcon"></i>
|
|
</div>
|
|
<div>
|
|
${vm.strings.get('SUCCESSFUL_CREATION', copiedProj.name)}
|
|
</div>
|
|
</div>`,
|
|
dismissButton: false,
|
|
dismissOnTimeout: true
|
|
});
|
|
reloadList();
|
|
})
|
|
.catch(createErrorHandler('copy project', 'GET'))
|
|
.finally(() => Wait('stop'));
|
|
};
|
|
|
|
vm.deleteProject = (id, name) => {
|
|
const action = () => {
|
|
$('#prompt-modal').modal('hide');
|
|
Wait('start');
|
|
ProjectModel
|
|
.request('delete', id)
|
|
.then(() => {
|
|
let reloadListStateParams = null;
|
|
|
|
if (vm.projects.length === 1
|
|
&& $state.params.project_search
|
|
&& _.has($state, 'params.project_search.page')
|
|
&& $state.params.project_search.page !== '1') {
|
|
reloadListStateParams = _.cloneDeep($state.params);
|
|
reloadListStateParams.project_search.page =
|
|
(parseInt(reloadListStateParams.project_search.page, 10) - 1).toString();
|
|
}
|
|
|
|
if (parseInt($state.params.project_id, 10) === id) {
|
|
$state.go('^', reloadListStateParams, { reload: true });
|
|
} else {
|
|
$state.go('.', reloadListStateParams, { reload: true });
|
|
}
|
|
})
|
|
.catch(createErrorHandler(`${ProjectModel.path}${id}/`, 'DELETE'))
|
|
.finally(() => {
|
|
Wait('stop');
|
|
});
|
|
};
|
|
|
|
ProjectModel.getDependentResourceCounts(id)
|
|
.then((counts) => {
|
|
const invalidateRelatedLines = [];
|
|
let deleteModalBody = `<div class="Prompt-bodyQuery">${vm.strings.get('deleteResource.CONFIRM', 'project')}</div>`;
|
|
|
|
counts.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) {
|
|
deleteModalBody = `<div class="Prompt-bodyQuery">${vm.strings.get('deleteResource.USED_BY', 'project')} ${vm.strings.get('deleteResource.CONFIRM', 'project')}</div>`;
|
|
invalidateRelatedLines.forEach(invalidateRelatedLine => {
|
|
deleteModalBody += invalidateRelatedLine;
|
|
});
|
|
}
|
|
|
|
Prompt({
|
|
hdr: vm.strings.get('DELETE'),
|
|
resourceName: $filter('sanitize')(name),
|
|
body: deleteModalBody,
|
|
action,
|
|
actionText: vm.strings.get('DELETE'),
|
|
});
|
|
});
|
|
};
|
|
|
|
vm.cancelUpdate = (project) => {
|
|
project.pending_cancellation = true;
|
|
Rest.setUrl(GetBasePath('projects') + project.id);
|
|
Rest.get()
|
|
.then(({ data }) => {
|
|
if (data.related.current_update) {
|
|
cancelSCMUpdate(data);
|
|
} else {
|
|
Alert(vm.strings.get('update.UPDATE_NOT_FOUND'), vm.strings.get('update.NO_RUNNING_UPDATE') + project.name, 'alert-info', undefined, undefined, undefined, undefined, true);
|
|
}
|
|
})
|
|
.catch(createErrorHandler('get project', 'GET'));
|
|
};
|
|
|
|
vm.SCMUpdate = (id, event) => {
|
|
try {
|
|
$(event.target).tooltip('hide');
|
|
} catch (e) {
|
|
// ignore
|
|
}
|
|
vm.projects.forEach((project) => {
|
|
if (project.id === id) {
|
|
if (project.scm_type === 'Manual' || (!project.scm_type)) {
|
|
// Do not respond. Button appears greyed out as if it is disabled.
|
|
// Not disabled though, because we need mouse over event
|
|
// to work. So user can click, but we just won't do anything.
|
|
// Alert('Missing SCM Setup', 'Before running an SCM update,
|
|
// edit the project and provide the SCM access information.', 'alert-info');
|
|
} else if (project.status === 'updating' || project.status === 'running' || project.status === 'pending') {
|
|
// Alert('Update in Progress', 'The SCM update process is running.
|
|
// Use the Refresh button to monitor the status.', 'alert-info');
|
|
} else {
|
|
ProjectUpdate({ scope: $scope, project_id: project.id });
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
function buildTooltips (project) {
|
|
project.statusIcon = getStatusIcon(project);
|
|
project.statusTip = getStatusTooltip(project);
|
|
project.scm_update_tooltip = vm.strings.get('update.GET_LATEST');
|
|
project.scm_update_disabled = false;
|
|
|
|
if (project.status === 'pending' || project.status === 'waiting') {
|
|
project.scm_update_disabled = true;
|
|
}
|
|
|
|
if (project.status === 'failed' && project.summary_fields.last_update && project.summary_fields.last_update.status === 'canceled') {
|
|
project.statusTip = vm.strings.get('status.UPDATE_CANCELED');
|
|
project.scm_update_disabled = true;
|
|
}
|
|
|
|
if (project.status === 'running' || project.status === 'updating') {
|
|
project.scm_update_tooltip = vm.strings.get('update.UPDATE_RUNNING');
|
|
project.scm_update_disabled = true;
|
|
}
|
|
|
|
if (project.scm_type === 'manual') {
|
|
project.statusIcon = 'none';
|
|
project.statusTip = vm.strings.get('status.NOT_CONFIG');
|
|
project.scm_update_tooltip = vm.strings.get('update.MANUAL_PROJECT_NO_UPDATE');
|
|
project.scm_update_disabled = true;
|
|
}
|
|
}
|
|
|
|
function cancelSCMUpdate (projectData) {
|
|
Rest.setUrl(projectData.related.current_update);
|
|
Rest.get()
|
|
.then(({ data }) => {
|
|
$scope.$emit('Check_Cancel', data);
|
|
})
|
|
.catch(createErrorHandler(projectData.related.current_update, 'GET'));
|
|
}
|
|
|
|
function reloadList () {
|
|
Wait('start');
|
|
const path = GetBasePath(vm.list.basePath) || GetBasePath(vm.list.name);
|
|
qs.search(path, $state.params.project_search)
|
|
.then((searchResponse) => {
|
|
vm.dataset = searchResponse.data;
|
|
vm.projects = vm.dataset.results;
|
|
})
|
|
.finally(() => Wait('stop'));
|
|
}
|
|
|
|
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 getStatusIcon (project) {
|
|
let icon = 'none';
|
|
switch (project.status) {
|
|
case 'n/a':
|
|
case 'ok':
|
|
case 'never updated':
|
|
icon = 'none';
|
|
break;
|
|
case 'pending':
|
|
case 'waiting':
|
|
case 'new':
|
|
icon = 'none';
|
|
break;
|
|
case 'updating':
|
|
case 'running':
|
|
icon = 'running';
|
|
break;
|
|
case 'successful':
|
|
icon = 'success';
|
|
break;
|
|
case 'failed':
|
|
case 'missing':
|
|
case 'canceled':
|
|
icon = 'error';
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return icon;
|
|
}
|
|
|
|
function getStatusTooltip (project) {
|
|
let tooltip = '';
|
|
switch (project.status) {
|
|
case 'n/a':
|
|
case 'ok':
|
|
case 'never updated':
|
|
tooltip = vm.strings.get('status.NEVER_UPDATE');
|
|
break;
|
|
case 'pending':
|
|
case 'waiting':
|
|
case 'new':
|
|
tooltip = vm.strings.get('status.UPDATE_QUEUED');
|
|
break;
|
|
case 'updating':
|
|
case 'running':
|
|
tooltip = vm.strings.get('status.UPDATE_RUNNING');
|
|
break;
|
|
case 'successful':
|
|
tooltip = vm.strings.get('status.UPDATE_SUCCESS');
|
|
break;
|
|
case 'failed':
|
|
tooltip = vm.strings.get('status.UPDATE_FAILED');
|
|
break;
|
|
case 'missing':
|
|
tooltip = vm.strings.get('status.UPDATE_MISSING');
|
|
break;
|
|
case 'canceled':
|
|
tooltip = vm.strings.get('status.UPDATE_CANCELED');
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return tooltip;
|
|
}
|
|
|
|
vm.isCollapsed = true;
|
|
|
|
vm.onCollapse = () => {
|
|
vm.isCollapsed = true;
|
|
};
|
|
|
|
vm.onExpand = () => {
|
|
vm.isCollapsed = false;
|
|
};
|
|
}
|
|
|
|
projectsListController.$inject = [
|
|
'$filter',
|
|
'$scope',
|
|
'$rootScope',
|
|
'$state',
|
|
'$log',
|
|
'Dataset',
|
|
'Alert',
|
|
'Rest',
|
|
'ProcessErrors',
|
|
'resolvedModels',
|
|
'ProjectsStrings',
|
|
'Wait',
|
|
'ngToast',
|
|
'Prompt',
|
|
'GetBasePath',
|
|
'QuerySet',
|
|
'ProjectUpdate',
|
|
];
|
|
|
|
export default projectsListController;
|