mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 03:10:42 -03:30
Merge pull request #1657 from jlmitch5/jobsNewListUi
implement new style jobs list in ui
This commit is contained in:
commit
ac70945071
@ -6,6 +6,7 @@ import atFeaturesApplications from '~features/applications';
|
||||
import atFeaturesCredentials from '~features/credentials';
|
||||
import atFeaturesTemplates from '~features/templates';
|
||||
import atFeaturesUsers from '~features/users';
|
||||
import atFeaturesJobs from '~features/jobs';
|
||||
|
||||
const MODULE_NAME = 'at.features';
|
||||
|
||||
@ -16,7 +17,8 @@ angular.module(MODULE_NAME, [
|
||||
atFeaturesApplications,
|
||||
atFeaturesCredentials,
|
||||
atFeaturesTemplates,
|
||||
atFeaturesUsers
|
||||
atFeaturesUsers,
|
||||
atFeaturesJobs
|
||||
]);
|
||||
|
||||
export default MODULE_NAME;
|
||||
|
||||
13
awx/ui/client/features/jobs/index.js
Normal file
13
awx/ui/client/features/jobs/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
import JobsStrings from './jobs.strings';
|
||||
import jobsRoute from './routes/jobs.route';
|
||||
|
||||
const MODULE_NAME = 'at.features.jobs';
|
||||
|
||||
angular
|
||||
.module(MODULE_NAME, [])
|
||||
.service('JobsStrings', JobsStrings)
|
||||
.run(['$stateExtender', ($stateExtender) => {
|
||||
$stateExtender.addState(jobsRoute);
|
||||
}]);
|
||||
|
||||
export default MODULE_NAME;
|
||||
19
awx/ui/client/features/jobs/index.view.html
Normal file
19
awx/ui/client/features/jobs/index.view.html
Normal file
@ -0,0 +1,19 @@
|
||||
<div class="tab-pane" id="jobs-page">
|
||||
<at-panel ng-cloak id="htmlTemplate">
|
||||
<div>
|
||||
<div ng-hide="$state.is('jobs.schedules')">
|
||||
<at-panel-heading hide-dismiss="true">
|
||||
<translate>JOBS</translate>
|
||||
</at-panel-heading>
|
||||
<div ui-view="jobsList"></div>
|
||||
</div>
|
||||
<div ng-hide="!$state.is('jobs.schedules')">
|
||||
<at-panel-heading hide-dismiss="true">
|
||||
<translate>SCHEDULES</translate>
|
||||
</at-panel-heading>
|
||||
<div ui-view="schedulesList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</at-panel>
|
||||
<div ng-include="'/static/partials/logviewer.html'"></div>
|
||||
</div>
|
||||
20
awx/ui/client/features/jobs/jobs.strings.js
Normal file
20
awx/ui/client/features/jobs/jobs.strings.js
Normal file
@ -0,0 +1,20 @@
|
||||
function JobsStrings (BaseString) {
|
||||
BaseString.call(this, 'jobs');
|
||||
|
||||
const { t } = this;
|
||||
const ns = this.jobs;
|
||||
|
||||
ns.list = {
|
||||
ROW_ITEM_LABEL_STARTED: t.s('Started'),
|
||||
ROW_ITEM_LABEL_FINISHED: t.s('Finished'),
|
||||
ROW_ITEM_LABEL_LAUNCHED_BY: t.s('Launched By'),
|
||||
ROW_ITEM_LABEL_JOB_TEMPLATE: t.s('Job Template'),
|
||||
ROW_ITEM_LABEL_INVENTORY: t.s('Inventory'),
|
||||
ROW_ITEM_LABEL_PROJECT: t.s('Project'),
|
||||
ROW_ITEM_LABEL_CREDENTIALS: t.s('Credentials'),
|
||||
};
|
||||
}
|
||||
|
||||
JobsStrings.$inject = ['BaseStringService'];
|
||||
|
||||
export default JobsStrings;
|
||||
137
awx/ui/client/features/jobs/jobsList.controller.js
Normal file
137
awx/ui/client/features/jobs/jobsList.controller.js
Normal file
@ -0,0 +1,137 @@
|
||||
/** ***********************************************
|
||||
* Copyright (c) 2018 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
************************************************ */
|
||||
const mapChoices = choices => Object
|
||||
.assign(...choices.map(([k, v]) => ({ [k]: v })));
|
||||
|
||||
function ListJobsController (
|
||||
$scope,
|
||||
$state,
|
||||
Dataset,
|
||||
resolvedModels,
|
||||
strings,
|
||||
qs,
|
||||
Prompt,
|
||||
$filter,
|
||||
ProcessErrors,
|
||||
Wait,
|
||||
Rest
|
||||
) {
|
||||
const vm = this || {};
|
||||
const [unifiedJob] = resolvedModels;
|
||||
|
||||
vm.strings = strings;
|
||||
|
||||
// smart-search
|
||||
const name = 'jobs';
|
||||
const iterator = 'job';
|
||||
const key = 'job_dataset';
|
||||
|
||||
$scope.list = { iterator, name };
|
||||
$scope.collection = { iterator, basePath: 'unified_jobs' };
|
||||
$scope[key] = Dataset.data;
|
||||
$scope[name] = Dataset.data.results;
|
||||
$scope.$on('updateDataset', (e, dataset) => {
|
||||
$scope[key] = dataset;
|
||||
$scope[name] = dataset.results;
|
||||
});
|
||||
$scope.$on('ws-jobs', () => {
|
||||
qs.search(unifiedJob.path, $state.params.job_search)
|
||||
.then(({ data }) => {
|
||||
$scope.$emit('updateDataset', data);
|
||||
});
|
||||
});
|
||||
|
||||
vm.jobTypes = mapChoices(unifiedJob
|
||||
.options('actions.GET.type.choices'));
|
||||
|
||||
vm.getLink = ({ type, id }) => {
|
||||
let link;
|
||||
|
||||
switch (type) {
|
||||
case 'job':
|
||||
link = `/#/jobs/${id}`;
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
link = `/#/ad_hoc_commands/${id}`;
|
||||
break;
|
||||
case 'system_job':
|
||||
link = `/#/management_jobs/${id}`;
|
||||
break;
|
||||
case 'project_update':
|
||||
link = `/#/scm_update/${id}`;
|
||||
break;
|
||||
case 'inventory_update':
|
||||
link = `/#/inventory_sync/${id}`;
|
||||
break;
|
||||
case 'workflow_job':
|
||||
link = `/#/workflows/${id}`;
|
||||
break;
|
||||
default:
|
||||
link = '';
|
||||
break;
|
||||
}
|
||||
|
||||
return link;
|
||||
};
|
||||
|
||||
vm.deleteJob = (job) => {
|
||||
const action = () => {
|
||||
$('#prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
Rest.setUrl(job.url);
|
||||
Rest.destroy()
|
||||
.then(() => {
|
||||
let reloadListStateParams = null;
|
||||
|
||||
if ($scope.jobs.length === 1 && $state.params.job_search &&
|
||||
!_.isEmpty($state.params.job_search.page) &&
|
||||
$state.params.job_search.page !== '1') {
|
||||
const page = `${(parseInt(reloadListStateParams
|
||||
.job_search.page, 10) - 1)}`;
|
||||
reloadListStateParams = _.cloneDeep($state.params);
|
||||
reloadListStateParams.job_search.page = page;
|
||||
}
|
||||
|
||||
$state.go('.', reloadListStateParams, { reload: true });
|
||||
})
|
||||
.catch(({ data, status }) => {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: strings.get('error.HEADER'),
|
||||
msg: strings.get('error.CALL', { path: `${job.url}`, status })
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
Wait('stop');
|
||||
});
|
||||
};
|
||||
|
||||
const deleteModalBody = `<div class="Prompt-bodyQuery">${strings.get('deleteResource.CONFIRM', 'job')}</div>`;
|
||||
|
||||
Prompt({
|
||||
hdr: strings.get('deleteResource.HEADER'),
|
||||
resourceName: $filter('sanitize')(job.name),
|
||||
body: deleteModalBody,
|
||||
action,
|
||||
actionText: 'DELETE'
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
ListJobsController.$inject = [
|
||||
'$scope',
|
||||
'$state',
|
||||
'Dataset',
|
||||
'resolvedModels',
|
||||
'JobsStrings',
|
||||
'QuerySet',
|
||||
'Prompt',
|
||||
'$filter',
|
||||
'ProcessErrors',
|
||||
'Wait',
|
||||
'Rest'
|
||||
];
|
||||
|
||||
export default ListJobsController;
|
||||
82
awx/ui/client/features/jobs/jobsList.view.html
Normal file
82
awx/ui/client/features/jobs/jobsList.view.html
Normal file
@ -0,0 +1,82 @@
|
||||
<at-panel-body>
|
||||
<div class="at-List-toolbar">
|
||||
<smart-search
|
||||
class="at-List-search"
|
||||
django-model="jobs"
|
||||
base-path="unified_jobs"
|
||||
iterator="job"
|
||||
list="list"
|
||||
dataset="job_dataset"
|
||||
collection="collection"
|
||||
search-tags="searchTags"
|
||||
query-set="querySet">
|
||||
</smart-search>
|
||||
</div>
|
||||
<at-list results="jobs">
|
||||
<!-- TODO: implement resources are missing red indicator as present in mockup -->
|
||||
<at-row ng-repeat="job in jobs" job-id="{{ job.id }}">
|
||||
<div class="at-Row-items">
|
||||
<!-- TODO: include workflow tab as well -->
|
||||
<at-row-item
|
||||
status="{{ job.status }}"
|
||||
status-tip="Job {{job.status}}. Click for details."
|
||||
header-value="{{ job.name }}"
|
||||
header-link="{{ vm.getLink(job) }}"
|
||||
header-tag="{{ vm.jobTypes[job.type] }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_STARTED') }}"
|
||||
value="{{ job.started | longDate }}"
|
||||
inline="true">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_FINISHED') }}"
|
||||
value="{{ job.finished | longDate }}"
|
||||
inline="true">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_LAUNCHED_BY') }}"
|
||||
value="{{ job.summary_fields.created_by.username }}"
|
||||
value-link="/#/users/{{ job.summary_fields.created_by.id }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_JOB_TEMPLATE') }}"
|
||||
value="{{ job.summary_fields.job_template.name }}"
|
||||
value-link="/#/templates/job_template/{{ job.summary_fields.job_template.id }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_INVENTORY') }}"
|
||||
value="{{ job.summary_fields.inventory.name }}"
|
||||
value-link="/#/inventories/{{ job.summary_fields.inventory.id }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_PROJECT') }}"
|
||||
value="{{ job.summary_fields.project.name }}"
|
||||
value-link="/#/projects/{{ job.summary_fields.project.id }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_CREDENTIALS') }}"
|
||||
tag-values="job.summary_fields.credentials"
|
||||
tags-are-creds="true">
|
||||
</at-row-item>
|
||||
<labels-list class="LabelList" show-delete="false" is-row-item="true">
|
||||
</labels-list>
|
||||
</div>
|
||||
<div class="at-Row-actions">
|
||||
<at-relaunch job="job"
|
||||
ng-show="job.summary_fields.user_capabilities.start">
|
||||
</at-relaunch>
|
||||
<at-row-action icon="fa-trash" ng-click="vm.deleteJob(job)"
|
||||
ng-show="job.summary_fields.user_capabilities.delete">
|
||||
</at-row-action>
|
||||
</div>
|
||||
</at-row>
|
||||
</at-list>
|
||||
<paginate
|
||||
collection="collection"
|
||||
dataset="job_dataset"
|
||||
iterator="job"
|
||||
base-path="unified_jobs"
|
||||
query-set="querySet">
|
||||
</paginate>
|
||||
</at-panel-body>
|
||||
@ -0,0 +1,64 @@
|
||||
import listContainerController from '~src/instance-groups/jobs/instanceGroupsJobsListContainer.controller';
|
||||
import { N_ } from '../../../src/i18n';
|
||||
import jobsListController from '../jobsList.controller';
|
||||
|
||||
const jobsListTemplate = require('~features/jobs/jobsList.view.html');
|
||||
const listContainerTemplate = require('~src/instance-groups/jobs/instanceGroupsJobsListContainer.partial.html');
|
||||
|
||||
export default {
|
||||
name: 'instanceGroups.jobs',
|
||||
url: '/:instance_group_id/jobs',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'instanceGroups.edit',
|
||||
label: N_('JOBS')
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '10',
|
||||
order_by: '-finished'
|
||||
},
|
||||
dynamic: true
|
||||
}
|
||||
},
|
||||
views: {
|
||||
'instanceGroupsJobsContainer@instanceGroups': {
|
||||
templateUrl: listContainerTemplate,
|
||||
controller: listContainerController,
|
||||
controllerAs: 'vm'
|
||||
},
|
||||
'jobsList@instanceGroups.jobs': {
|
||||
templateUrl: jobsListTemplate,
|
||||
controller: jobsListController,
|
||||
controllerAs: 'vm'
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
resolvedModels: [
|
||||
'UnifiedJobModel',
|
||||
(UnifiedJob) => {
|
||||
const models = [
|
||||
new UnifiedJob(['options']),
|
||||
];
|
||||
return Promise.all(models);
|
||||
},
|
||||
],
|
||||
Dataset: [
|
||||
'$stateParams',
|
||||
'Wait',
|
||||
'GetBasePath',
|
||||
'QuerySet',
|
||||
($stateParams, Wait, GetBasePath, qs) => {
|
||||
const groupId = $stateParams.instance_group_id;
|
||||
|
||||
const searchParam = $stateParams.job_search;
|
||||
|
||||
const searchPath = `api/v2/instance_groups/${groupId}/jobs`;
|
||||
|
||||
Wait('start');
|
||||
return qs.search(searchPath, searchParam)
|
||||
.finally(() => Wait('stop'));
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
64
awx/ui/client/features/jobs/routes/instanceJobs.route.js
Normal file
64
awx/ui/client/features/jobs/routes/instanceJobs.route.js
Normal file
@ -0,0 +1,64 @@
|
||||
import listContainerController from '~src/instance-groups/jobs/instanceJobsListContainer.controller';
|
||||
import { N_ } from '../../../src/i18n';
|
||||
import jobsListController from '../jobsList.controller';
|
||||
|
||||
const jobsListTemplate = require('~features/jobs/jobsList.view.html');
|
||||
const listContainerTemplate = require('~src/instance-groups/jobs/instanceJobsListContainer.partial.html');
|
||||
|
||||
export default {
|
||||
name: 'instanceGroups.instanceJobs',
|
||||
url: '/:instance_group_id/instances/:instance_id/jobs',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'instanceGroups.edit',
|
||||
label: N_('JOBS')
|
||||
},
|
||||
views: {
|
||||
'instanceJobsContainer@instanceGroups': {
|
||||
templateUrl: listContainerTemplate,
|
||||
controller: listContainerController,
|
||||
controllerAs: 'vm'
|
||||
},
|
||||
'jobsList@instanceGroups.instanceJobs': {
|
||||
templateUrl: jobsListTemplate,
|
||||
controller: jobsListController,
|
||||
controllerAs: 'vm'
|
||||
},
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '10',
|
||||
order_by: '-finished'
|
||||
},
|
||||
dynamic: true
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
resolvedModels: [
|
||||
'UnifiedJobModel',
|
||||
(UnifiedJob) => {
|
||||
const models = [
|
||||
new UnifiedJob(['options']),
|
||||
];
|
||||
return Promise.all(models);
|
||||
},
|
||||
],
|
||||
Dataset: [
|
||||
'$stateParams',
|
||||
'Wait',
|
||||
'GetBasePath',
|
||||
'QuerySet',
|
||||
($stateParams, Wait, GetBasePath, qs) => {
|
||||
const instanceId = $stateParams.instance_id;
|
||||
|
||||
const searchParam = $stateParams.job_search;
|
||||
|
||||
const searchPath = `api/v2/instances/${instanceId}/jobs`;
|
||||
|
||||
Wait('start');
|
||||
return qs.search(searchPath, searchParam)
|
||||
.finally(() => Wait('stop'));
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,63 @@
|
||||
import { N_ } from '../../../src/i18n';
|
||||
import jobsListController from '../jobsList.controller';
|
||||
|
||||
const jobsListTemplate = require('~features/jobs/jobsList.view.html');
|
||||
|
||||
export default {
|
||||
url: '/completed_jobs',
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '20',
|
||||
or__job__inventory: '',
|
||||
or__adhoccommand__inventory: '',
|
||||
or__inventoryupdate__inventory_source__inventory: '',
|
||||
order_by: '-id'
|
||||
},
|
||||
dynamic: true,
|
||||
squash: ''
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: N_('JOBS')
|
||||
},
|
||||
views: {
|
||||
related: {
|
||||
templateUrl: jobsListTemplate,
|
||||
controller: jobsListController,
|
||||
controllerAs: 'vm'
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
resolvedModels: [
|
||||
'UnifiedJobModel',
|
||||
(UnifiedJob) => {
|
||||
const models = [
|
||||
new UnifiedJob(['options']),
|
||||
];
|
||||
return Promise.all(models);
|
||||
},
|
||||
],
|
||||
Dataset: [
|
||||
'$stateParams',
|
||||
'Wait',
|
||||
'GetBasePath',
|
||||
'QuerySet',
|
||||
($stateParams, Wait, GetBasePath, qs) => {
|
||||
const inventoryId = $stateParams.inventory_id ?
|
||||
$stateParams.inventory_id : $stateParams.smartinventory_id;
|
||||
|
||||
const searchParam = _.assign($stateParams.job_search, {
|
||||
or__job__inventory: inventoryId,
|
||||
or__adhoccommand__inventory: inventoryId,
|
||||
or__inventoryupdate__inventory_source__inventory: inventoryId });
|
||||
|
||||
const searchPath = GetBasePath('unified_jobs');
|
||||
|
||||
Wait('start');
|
||||
return qs.search(searchPath, searchParam)
|
||||
.finally(() => Wait('stop'));
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
67
awx/ui/client/features/jobs/routes/jobs.route.js
Normal file
67
awx/ui/client/features/jobs/routes/jobs.route.js
Normal file
@ -0,0 +1,67 @@
|
||||
import { N_ } from '../../../src/i18n';
|
||||
import jobsListController from '../jobsList.controller';
|
||||
|
||||
const indexTemplate = require('~features/jobs/index.view.html');
|
||||
const jobsListTemplate = require('~features/jobs/jobsList.view.html');
|
||||
|
||||
export default {
|
||||
searchPrefix: 'job',
|
||||
name: 'jobs',
|
||||
url: '/jobs',
|
||||
ncyBreadcrumb: {
|
||||
label: N_('JOBS')
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
not__launch_type: 'sync',
|
||||
order_by: '-finished'
|
||||
},
|
||||
dynamic: true,
|
||||
squash: false
|
||||
}
|
||||
},
|
||||
data: {
|
||||
socket: {
|
||||
groups: {
|
||||
jobs: ['status_changed'],
|
||||
schedules: ['changed']
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
resolvedModels: [
|
||||
'UnifiedJobModel',
|
||||
(UnifiedJob) => {
|
||||
const models = [
|
||||
new UnifiedJob(['options']),
|
||||
];
|
||||
return Promise.all(models);
|
||||
},
|
||||
],
|
||||
Dataset: [
|
||||
'$stateParams',
|
||||
'Wait',
|
||||
'GetBasePath',
|
||||
'QuerySet',
|
||||
($stateParams, Wait, GetBasePath, qs) => {
|
||||
const searchParam = $stateParams.job_search;
|
||||
const searchPath = GetBasePath('unified_jobs');
|
||||
|
||||
Wait('start');
|
||||
return qs.search(searchPath, searchParam)
|
||||
.finally(() => Wait('stop'));
|
||||
}
|
||||
],
|
||||
},
|
||||
views: {
|
||||
'@': {
|
||||
templateUrl: indexTemplate
|
||||
},
|
||||
'jobsList@jobs': {
|
||||
templateUrl: jobsListTemplate,
|
||||
controller: jobsListController,
|
||||
controllerAs: 'vm'
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,50 @@
|
||||
import jobsListController from '../jobsList.controller';
|
||||
|
||||
const jobsListTemplate = require('~features/jobs/jobsList.view.html');
|
||||
|
||||
export default {
|
||||
name: 'portalMode.allJobs',
|
||||
url: '/alljobs?{job_search:queryset}',
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '20',
|
||||
order_by: '-finished'
|
||||
},
|
||||
dynamic: true
|
||||
}
|
||||
},
|
||||
views: {
|
||||
'jobs@portalMode': {
|
||||
templateUrl: jobsListTemplate,
|
||||
controller: jobsListController,
|
||||
controllerAs: 'vm'
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
resolvedModels: [
|
||||
'UnifiedJobModel',
|
||||
(UnifiedJob) => {
|
||||
const models = [
|
||||
new UnifiedJob(['options']),
|
||||
];
|
||||
return Promise.all(models);
|
||||
},
|
||||
],
|
||||
Dataset: [
|
||||
'$stateParams',
|
||||
'Wait',
|
||||
'GetBasePath',
|
||||
'QuerySet',
|
||||
($stateParams, Wait, GetBasePath, qs) => {
|
||||
const searchParam = $stateParams.job_search;
|
||||
|
||||
const searchPath = GetBasePath('unified_jobs');
|
||||
|
||||
Wait('start');
|
||||
return qs.search(searchPath, searchParam)
|
||||
.finally(() => Wait('stop'));
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
53
awx/ui/client/features/jobs/routes/portalModeMyJobs.route.js
Normal file
53
awx/ui/client/features/jobs/routes/portalModeMyJobs.route.js
Normal file
@ -0,0 +1,53 @@
|
||||
import jobsListController from '../jobsList.controller';
|
||||
|
||||
const jobsListTemplate = require('~features/jobs/jobsList.view.html');
|
||||
|
||||
export default {
|
||||
name: 'portalMode.myJobs',
|
||||
url: '/myjobs?{job_search:queryset}',
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '20',
|
||||
order_by: '-finished',
|
||||
created_by: null
|
||||
},
|
||||
dynamic: true
|
||||
}
|
||||
},
|
||||
views: {
|
||||
'jobs@portalMode': {
|
||||
templateUrl: jobsListTemplate,
|
||||
controller: jobsListController,
|
||||
controllerAs: 'vm'
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
resolvedModels: [
|
||||
'UnifiedJobModel',
|
||||
(UnifiedJob) => {
|
||||
const models = [
|
||||
new UnifiedJob(['options']),
|
||||
];
|
||||
return Promise.all(models);
|
||||
},
|
||||
],
|
||||
Dataset: [
|
||||
'$stateParams',
|
||||
'Wait',
|
||||
'GetBasePath',
|
||||
'QuerySet',
|
||||
'$rootScope',
|
||||
($stateParams, Wait, GetBasePath, qs, $rootScope) => {
|
||||
const searchParam = _.assign($stateParams.job_search, {
|
||||
created_by: $rootScope.current_user.id });
|
||||
|
||||
const searchPath = GetBasePath('unified_jobs');
|
||||
|
||||
Wait('start');
|
||||
return qs.search(searchPath, searchParam)
|
||||
.finally(() => Wait('stop'));
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,60 @@
|
||||
import { N_ } from '../../../src/i18n';
|
||||
import jobsListController from '../jobsList.controller';
|
||||
|
||||
const jobsListTemplate = require('~features/jobs/jobsList.view.html');
|
||||
|
||||
export default {
|
||||
url: '/completed_jobs',
|
||||
name: 'templates.editJobTemplate.completed_jobs',
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '20',
|
||||
job__job_template: '',
|
||||
order_by: '-id'
|
||||
},
|
||||
dynamic: true,
|
||||
squash: ''
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: N_('COMPLETED JOBS')
|
||||
},
|
||||
views: {
|
||||
related: {
|
||||
templateUrl: jobsListTemplate,
|
||||
controller: jobsListController,
|
||||
controllerAs: 'vm'
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
resolvedModels: [
|
||||
'UnifiedJobModel',
|
||||
(UnifiedJob) => {
|
||||
const models = [
|
||||
new UnifiedJob(['options']),
|
||||
];
|
||||
return Promise.all(models);
|
||||
},
|
||||
],
|
||||
Dataset: [
|
||||
'$stateParams',
|
||||
'Wait',
|
||||
'GetBasePath',
|
||||
'QuerySet',
|
||||
($stateParams, Wait, GetBasePath, qs) => {
|
||||
const templateId = $stateParams.job_template_id ?
|
||||
$stateParams.job_template_id : $stateParams.job_template_id;
|
||||
|
||||
const searchParam = _.assign($stateParams
|
||||
.job_search, { job__job_template: templateId });
|
||||
|
||||
const searchPath = GetBasePath('unified_jobs');
|
||||
|
||||
Wait('start');
|
||||
return qs.search(searchPath, searchParam)
|
||||
.finally(() => Wait('stop'));
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@ -151,6 +151,10 @@
|
||||
line-height: @at-height-list-row-item;
|
||||
}
|
||||
|
||||
.at-RowItem-status {
|
||||
margin-right: @at-margin-right-list-row-item-status;
|
||||
}
|
||||
|
||||
.at-RowItem--isHeader {
|
||||
color: @at-color-body-text;
|
||||
margin-bottom: @at-margin-bottom-list-header;
|
||||
@ -263,6 +267,16 @@
|
||||
margin: 2px 20px 0 0;
|
||||
}
|
||||
|
||||
.at-RowItem--inline {
|
||||
display: inline-flex;
|
||||
margin-right: @at-margin-right-list-row-item-inline;
|
||||
|
||||
.at-RowItem-label {
|
||||
width: auto;
|
||||
margin-right: @at-margin-right-list-row-item-inline-label;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: @at-breakpoint-compact-list) {
|
||||
.at-Row-actions {
|
||||
flex-direction: column;
|
||||
@ -271,4 +285,14 @@
|
||||
.at-RowAction {
|
||||
margin: @at-margin-list-row-action-mobile;
|
||||
}
|
||||
|
||||
.at-RowItem--inline {
|
||||
display: flex;
|
||||
margin-right: inherit;
|
||||
|
||||
.at-RowItem-label {
|
||||
width: @at-width-list-row-item-label;
|
||||
margin-right: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,13 @@ function atRowItem () {
|
||||
transclude: true,
|
||||
templateUrl,
|
||||
scope: {
|
||||
inline: '@',
|
||||
badge: '@',
|
||||
headerValue: '@',
|
||||
headerLink: '@',
|
||||
headerTag: '@',
|
||||
status: '@',
|
||||
statusTip: '@',
|
||||
labelValue: '@',
|
||||
labelLink: '@',
|
||||
labelState: '@',
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
<div class="at-RowItem" ng-class="{'at-RowItem--isHeader': headerValue}"
|
||||
ng-show="headerValue || value || (smartStatus && smartStatus.summary_fields.recent_jobs.length) || (tagValues && tagValues.length)">
|
||||
<div class="at-RowItem" ng-class="{'at-RowItem--isHeader': headerValue, 'at-RowItem--inline': inline}"
|
||||
ng-show="status || headerValue || value || (smartStatus && smartStatus.summary_fields.recent_jobs.length) || (tagValues && tagValues.length)">
|
||||
<div class="at-RowItem-status" ng-if="status">
|
||||
<a ng-if="headerLink" ng-href="{{ headerLink }}"
|
||||
aw-tool-tip="{{ statusTip }}" aw-tip-watch="statusTip"
|
||||
data-placement="top">
|
||||
<i class="fa icon-job-{{ status }}"></i>
|
||||
</a>
|
||||
<i ng-if="!headerLink" class="fa icon-job-{{ status }}"></i>
|
||||
</div>
|
||||
<div class="at-RowItem-header" ng-if="headerValue && headerLink">
|
||||
<a ng-href="{{ headerLink }}">{{ headerValue }}</a>
|
||||
</div>
|
||||
@ -41,4 +49,4 @@
|
||||
{{ tag.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -29,4 +29,8 @@
|
||||
|
||||
.at-TabGroup + .at-Panel-body {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.at-TabGroup--padBelow {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
21
awx/ui/client/lib/models/UnifiedJob.js
Normal file
21
awx/ui/client/lib/models/UnifiedJob.js
Normal file
@ -0,0 +1,21 @@
|
||||
let Base;
|
||||
|
||||
function UnifiedJobModel (method, resource, config) {
|
||||
Base.call(this, 'unified_jobs');
|
||||
|
||||
this.Constructor = UnifiedJobModel;
|
||||
|
||||
return this.create(method, resource, config);
|
||||
}
|
||||
|
||||
function UnifiedJobModelLoader (BaseModel) {
|
||||
Base = BaseModel;
|
||||
|
||||
return UnifiedJobModel;
|
||||
}
|
||||
|
||||
UnifiedJobModelLoader.$inject = [
|
||||
'BaseModel'
|
||||
];
|
||||
|
||||
export default UnifiedJobModelLoader;
|
||||
@ -23,6 +23,7 @@ import UnifiedJobTemplate from '~models/UnifiedJobTemplate';
|
||||
import WorkflowJob from '~models/WorkflowJob';
|
||||
import WorkflowJobTemplate from '~models/WorkflowJobTemplate';
|
||||
import WorkflowJobTemplateNode from '~models/WorkflowJobTemplateNode';
|
||||
import UnifiedJob from '~models/UnifiedJob';
|
||||
|
||||
const MODULE_NAME = 'at.lib.models';
|
||||
|
||||
@ -49,6 +50,7 @@ angular
|
||||
.service('OrganizationModel', Organization)
|
||||
.service('ProjectModel', Project)
|
||||
.service('ScheduleModel', Schedule)
|
||||
.service('UnifiedJobModel', UnifiedJob)
|
||||
.service('UnifiedJobTemplateModel', UnifiedJobTemplate)
|
||||
.service('WorkflowJobModel', WorkflowJob)
|
||||
.service('WorkflowJobTemplateModel', WorkflowJobTemplate)
|
||||
|
||||
@ -262,6 +262,9 @@
|
||||
@at-margin-right-list-row-item-tag-icon: 8px;
|
||||
@at-margin-left-list-row-item-tag-container: -10px;
|
||||
@at-margin-list-row-action-mobile: 10px;
|
||||
@at-margin-right-list-row-item-status: @at-space-2x;
|
||||
@at-margin-right-list-row-item-inline: @at-space-4x;
|
||||
@at-margin-right-list-row-item-inline-label: @at-space-2x;
|
||||
|
||||
@at-height-divider: @at-margin-panel;
|
||||
@at-height-input: 30px;
|
||||
|
||||
@ -33,7 +33,6 @@ import login from './login/main';
|
||||
import activityStream from './activity-stream/main';
|
||||
import standardOut from './standard-out/main';
|
||||
import Templates from './templates/main';
|
||||
import jobs from './jobs/main';
|
||||
import teams from './teams/main';
|
||||
import users from './users/main';
|
||||
import projects from './projects/main';
|
||||
@ -92,7 +91,6 @@ angular
|
||||
standardOut.name,
|
||||
Templates.name,
|
||||
portalMode.name,
|
||||
jobs.name,
|
||||
teams.name,
|
||||
users.name,
|
||||
projects.name,
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
|
||||
<div ui-view="instances"></div>
|
||||
|
||||
<div ui-view="jobs"></div>
|
||||
<div ui-view="instanceJobsContainer"></div>
|
||||
|
||||
<div ui-view="instanceGroupsJobsContainer"></div>
|
||||
|
||||
<div ui-view="list"></div>
|
||||
</div>
|
||||
|
||||
@ -23,8 +23,12 @@ function InstanceGroupsStrings (BaseString) {
|
||||
IS_OFFLINE: t.s('Unavailable to run jobs.'),
|
||||
IS_OFFLINE_LABEL: t.s('Unavailable')
|
||||
};
|
||||
|
||||
ns.jobs = {
|
||||
PANEL_TITLE: t.s('Jobs')
|
||||
};
|
||||
}
|
||||
|
||||
InstanceGroupsStrings.$inject = ['BaseStringService'];
|
||||
|
||||
export default InstanceGroupsStrings;
|
||||
export default InstanceGroupsStrings;
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
|
||||
function InstanceJobsController ($scope, $filter, $state, model, strings, jobStrings, Instance) {
|
||||
const vm = this || {};
|
||||
let { instance } = model;
|
||||
const instance_id = instance.get('id');
|
||||
|
||||
init();
|
||||
|
||||
function init(){
|
||||
vm.strings = strings;
|
||||
vm.jobStrings = jobStrings;
|
||||
vm.queryset = { page_size: '10', order_by: '-finished'};
|
||||
vm.jobs = instance.get('related.jobs.results');
|
||||
vm.dataset = instance.get('related.jobs');
|
||||
vm.count = instance.get('related.jobs.count');
|
||||
vm.panelTitle = `${jobStrings.get('list.PANEL_TITLE')} | ${instance.get('hostname')}`;
|
||||
|
||||
vm.tab = {
|
||||
details: {_hide: true},
|
||||
instances: {_hide: true},
|
||||
jobs: {_hide: true}
|
||||
};
|
||||
}
|
||||
|
||||
vm.getTime = function(time) {
|
||||
let val = "";
|
||||
if (time) {
|
||||
val += $filter('longDate')(time);
|
||||
}
|
||||
if (val === "") {
|
||||
val = undefined;
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
$scope.isSuccessful = function (status) {
|
||||
return (status === "successful");
|
||||
};
|
||||
|
||||
$scope.viewjobResults = function(job) {
|
||||
var goTojobResults = function(state) {
|
||||
$state.go(state, { id: job.id }, { reload: true });
|
||||
};
|
||||
switch (job.type) {
|
||||
case 'job':
|
||||
goTojobResults('jobResult');
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
goTojobResults('adHocJobStdout');
|
||||
break;
|
||||
case 'system_job':
|
||||
goTojobResults('managementJobStdout');
|
||||
break;
|
||||
case 'project_update':
|
||||
goTojobResults('scmUpdateStdout');
|
||||
break;
|
||||
case 'inventory_update':
|
||||
goTojobResults('inventorySyncStdout');
|
||||
break;
|
||||
case 'workflow_job':
|
||||
goTojobResults('workflowResults');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$on('ws-jobs', () => {
|
||||
new Instance(['get', 'options'], [instance_id, instance_id])
|
||||
.then((data) => {
|
||||
return data.extend('get', 'jobs', {params: {page_size: "10", order_by: "-finished"}});
|
||||
})
|
||||
.then((data) => {
|
||||
instance = data;
|
||||
init();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
InstanceJobsController.$inject = [
|
||||
'$scope',
|
||||
'$filter',
|
||||
'$state',
|
||||
'resolvedModels',
|
||||
'InstanceGroupsStrings',
|
||||
'JobStrings',
|
||||
'InstanceModel'
|
||||
];
|
||||
|
||||
export default InstanceJobsController;
|
||||
@ -0,0 +1,38 @@
|
||||
|
||||
function InstanceGroupJobsContainerController (strings, $state) {
|
||||
const vm = this || {};
|
||||
|
||||
init();
|
||||
function init() {
|
||||
const instanceGroupId = $state.params.instance_group_id;
|
||||
|
||||
vm.panelTitle = strings.get('jobs.PANEL_TITLE');
|
||||
vm.strings = strings;
|
||||
|
||||
vm.tab = {
|
||||
details: {
|
||||
_go: 'instanceGroups.edit',
|
||||
_params: { instance_group_id: instanceGroupId },
|
||||
_label: strings.get('tab.DETAILS')
|
||||
},
|
||||
instances: {
|
||||
_go: 'instanceGroups.instances',
|
||||
_params: { instance_group_id: instanceGroupId },
|
||||
_label: strings.get('tab.INSTANCES')
|
||||
},
|
||||
jobs: {
|
||||
_active: true,
|
||||
_params: { instance_group_id: instanceGroupId },
|
||||
_label: strings.get('tab.JOBS')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
InstanceGroupJobsContainerController.$inject = [
|
||||
'InstanceGroupsStrings',
|
||||
'$state'
|
||||
];
|
||||
|
||||
export default InstanceGroupJobsContainerController;
|
||||
@ -0,0 +1,11 @@
|
||||
<at-panel>
|
||||
<at-panel-heading>
|
||||
{{ vm.panelTitle }}
|
||||
</at-panel-heading>
|
||||
<at-tab-group class="at-TabGroup--padBelow">
|
||||
<at-tab state="vm.tab.details">{{:: vm.strings.get('tab.DETAILS') }}</at-tab>
|
||||
<at-tab state="vm.tab.instances">{{:: vm.strings.get('tab.INSTANCES') }}</at-tab>
|
||||
<at-tab state="vm.tab.jobs">{{:: vm.strings.get('tab.JOBS') }}</at-tab>
|
||||
</at-tab-group>
|
||||
<div ui-view="jobsList"></div>
|
||||
</at-panel>
|
||||
@ -0,0 +1,17 @@
|
||||
|
||||
function InstanceGroupJobsContainerController (strings) {
|
||||
const vm = this || {};
|
||||
|
||||
init();
|
||||
function init() {
|
||||
vm.panelTitle = strings.get('jobs.PANEL_TITLE');
|
||||
vm.strings = strings;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
InstanceGroupJobsContainerController.$inject = [
|
||||
'InstanceGroupsStrings'
|
||||
];
|
||||
|
||||
export default InstanceGroupJobsContainerController;
|
||||
@ -0,0 +1,6 @@
|
||||
<at-panel>
|
||||
<at-panel-heading>
|
||||
{{ vm.panelTitle }}
|
||||
</at-panel-heading>
|
||||
<div ui-view="jobsList"></div>
|
||||
</at-panel>
|
||||
@ -1,86 +0,0 @@
|
||||
<at-panel>
|
||||
<at-panel-heading>
|
||||
{{ vm.panelTitle }}
|
||||
</at-panel-heading>
|
||||
<at-tab-group>
|
||||
<at-tab state="vm.tab.details">{{:: vm.strings.get('tab.DETAILS') }}</at-tab>
|
||||
<at-tab state="vm.tab.instances">{{:: vm.strings.get('tab.INSTANCES') }}</at-tab>
|
||||
<at-tab state="vm.tab.jobs">{{:: vm.strings.get('tab.JOBS') }}</at-tab>
|
||||
</at-tab-group>
|
||||
|
||||
<at-panel-body>
|
||||
<div class="at-List-toolbar">
|
||||
<smart-search
|
||||
class="at-List-search"
|
||||
django-model="jobs"
|
||||
base-path="unified_jobs"
|
||||
iterator="job"
|
||||
list="list"
|
||||
dataset="job_dataset"
|
||||
collection="collection"
|
||||
search-tags="searchTags"
|
||||
query-set="querySet">
|
||||
</smart-search>
|
||||
</div>
|
||||
<at-list results="vm.jobs">
|
||||
<at-row ng-repeat="job in vm.jobs"
|
||||
ng-class="{'at-Row--active': (job.id === vm.activeId)}"
|
||||
job-id="{{ job.id }}">
|
||||
<div class="at-RowStatus">
|
||||
<a ng-click="vm.viewJobResults(job)" aw-tool-tip="{{ job.status_tip }}" data-tip-watch="job.status_tip" aw-tip-placement="right" >
|
||||
<i class="fa icon-job-{{ job.status }}"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="at-Row-items">
|
||||
<at-row-item
|
||||
class="at-RowItem--isHeaderLink"
|
||||
header-value="{{ job.name }}"
|
||||
header-tag="{{ job.type }}"
|
||||
ng-click="vm.viewjobResults(job)">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.jobStrings.get('list.ROW_ITEM_LABEL_STARTED') }}"
|
||||
value="{{ vm.getTime(job.started) }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.jobStrings.get('list.ROW_ITEM_LABEL_FINISHED') }}"
|
||||
value="{{ vm.getTime(job.finished) }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
header-value="{{ job.name }}"
|
||||
header-link="/#/jobs/workflow_job/{{ job.id }}"
|
||||
header-tag="{{ vm.jobTypes[job.type] }}"
|
||||
ng-if="job.type === 'workflow_job_job'">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.jobStrings.get('list.ROW_ITEM_LABEL_TEMPLATE') }}"
|
||||
value="{{ job.summary_fields.job_template.name }}"
|
||||
value-link="/#/templates/job_template/{{ job.summary_fields.job_template.id }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.jobStrings.get('list.ROW_ITEM_LABEL_INVENTORY') }}"
|
||||
value="{{ job.summary_fields.inventory.name }}"
|
||||
value-link="/#/inventories/inventory/{{ job.summary_fields.inventory.id }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.jobStrings.get('list.ROW_ITEM_LABEL_PROJECT') }}"
|
||||
value="{{ job.summary_fields.project.name }}"
|
||||
value-link="/#/projects/{{ job.summary_fields.project.id }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.jobStrings.get('list.ROW_ITEM_LABEL_CREDENTIALS') }}"
|
||||
tag-values="job.summary_fields.credentials"
|
||||
tags-are-creds="true">
|
||||
</at-row-item>
|
||||
</div>
|
||||
</at-row>
|
||||
</at-list>
|
||||
<paginate
|
||||
collection="vm.jobs"
|
||||
dataset="vm.dataset"
|
||||
iterator="job"
|
||||
base-path="unified_jobs"
|
||||
query-set="vm.queryset">
|
||||
</paginate>
|
||||
</at-panel-body>
|
||||
</at-panel>
|
||||
@ -1,100 +0,0 @@
|
||||
|
||||
function InstanceGroupJobsController ($scope, $filter, $state, model, strings, jobStrings, InstanceGroup) {
|
||||
const vm = this || {};
|
||||
let { instanceGroup } = model;
|
||||
const instance_group_id = instanceGroup.get('id');
|
||||
|
||||
init();
|
||||
|
||||
function init(){
|
||||
vm.strings = strings;
|
||||
vm.jobStrings = jobStrings;
|
||||
vm.queryset = { page_size: '10', order_by: '-finished', instance_group_id: instance_group_id };
|
||||
vm.jobs = instanceGroup.get('related.jobs.results');
|
||||
vm.dataset = instanceGroup.get('related.jobs');
|
||||
vm.count = instanceGroup.get('related.jobs.count');
|
||||
vm.panelTitle = instanceGroup.get('name');
|
||||
|
||||
vm.tab = {
|
||||
details: {
|
||||
_go: 'instanceGroups.edit',
|
||||
_params: { instance_group_id },
|
||||
_label: strings.get('tab.DETAILS')
|
||||
},
|
||||
instances: {
|
||||
_go: 'instanceGroups.instances',
|
||||
_params: { instance_group_id },
|
||||
_label: strings.get('tab.INSTANCES')
|
||||
},
|
||||
jobs: {
|
||||
_active: true,
|
||||
_label: strings.get('tab.JOBS')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
vm.getTime = function(time) {
|
||||
let val = "";
|
||||
if (time) {
|
||||
val += $filter('longDate')(time);
|
||||
}
|
||||
if (val === "") {
|
||||
val = undefined;
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
$scope.isSuccessful = function (status) {
|
||||
return (status === "successful");
|
||||
};
|
||||
|
||||
vm.viewjobResults = function(job) {
|
||||
var goTojobResults = function(state) {
|
||||
$state.go(state, { id: job.id }, { reload: true });
|
||||
};
|
||||
switch (job.type) {
|
||||
case 'job':
|
||||
goTojobResults('jobResult');
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
goTojobResults('adHocJobStdout');
|
||||
break;
|
||||
case 'system_job':
|
||||
goTojobResults('managementJobStdout');
|
||||
break;
|
||||
case 'project_update':
|
||||
goTojobResults('scmUpdateStdout');
|
||||
break;
|
||||
case 'inventory_update':
|
||||
goTojobResults('inventorySyncStdout');
|
||||
break;
|
||||
case 'workflow_job':
|
||||
goTojobResults('workflowResults');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$on('ws-jobs', () => {
|
||||
new InstanceGroup(['get', 'options'], [instance_group_id, instance_group_id])
|
||||
.then((instance_group) => {
|
||||
return instance_group.extend('get', 'jobs', {params: {page_size: "10", order_by: "-finished"}});
|
||||
})
|
||||
.then((instance_group) => {
|
||||
instanceGroup = instance_group;
|
||||
init();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
InstanceGroupJobsController.$inject = [
|
||||
'$scope',
|
||||
'$filter',
|
||||
'$state',
|
||||
'resolvedModels',
|
||||
'InstanceGroupsStrings',
|
||||
'JobStrings',
|
||||
'InstanceGroupModel'
|
||||
];
|
||||
|
||||
export default InstanceGroupJobsController;
|
||||
@ -1,76 +0,0 @@
|
||||
export default ['i18n', function (i18n) {
|
||||
return {
|
||||
name: 'jobs',
|
||||
iterator: 'job',
|
||||
basePath: 'api/v2/instance_groups/{{$stateParams.instance_group_id}}/jobs/',
|
||||
index: false,
|
||||
hover: false,
|
||||
well: true,
|
||||
emptyListText: i18n._('No jobs have yet run.'),
|
||||
listTitle: false,
|
||||
|
||||
fields: {
|
||||
status: {
|
||||
label: '',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus',
|
||||
dataTipWatch: 'job.status_tip',
|
||||
awToolTip: "{{ job.status_tip }}",
|
||||
awTipPlacement: "right",
|
||||
dataTitle: "{{ job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ job.status }}',
|
||||
iconOnly: true,
|
||||
ngClick: "viewjobResults(job)",
|
||||
nosort: true
|
||||
},
|
||||
id: {
|
||||
label: i18n._('ID'),
|
||||
ngClick: "viewjobResults(job)",
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||
awToolTip: "{{ job.status_tip }}",
|
||||
dataPlacement: 'top',
|
||||
noLink: true
|
||||
},
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
|
||||
ngClick: "viewjobResults(job)",
|
||||
badgePlacement: 'right',
|
||||
badgeCustom: true,
|
||||
nosort: true,
|
||||
badgeIcon: `<a href="{{ job.workflow_result_link }}"
|
||||
aw-tool-tip="{{'View workflow results'|translate}}"
|
||||
data-placement="top"
|
||||
data-original-title="" title="">
|
||||
<i class="WorkflowBadge"
|
||||
ng-show="job.launch_type === 'workflow' ">
|
||||
W
|
||||
</i>
|
||||
</a>`
|
||||
},
|
||||
type: {
|
||||
label: i18n._('Type'),
|
||||
ngBind: 'job.type_label',
|
||||
columnClass: "col-lg-2 hidden-md hidden-sm hidden-xs",
|
||||
nosort: true
|
||||
},
|
||||
finished: {
|
||||
label: i18n._('Finished'),
|
||||
noLink: true,
|
||||
filter: "longDate",
|
||||
columnClass: "col-lg-2 col-md-3 col-sm-3 hidden-xs",
|
||||
key: true,
|
||||
desc: true,
|
||||
nosort: true
|
||||
},
|
||||
labels: {
|
||||
label: i18n._('Labels'),
|
||||
type: 'labels',
|
||||
nosort: true,
|
||||
showDelete: false,
|
||||
columnClass: 'List-tableCell col-lg-4 col-md-4 hidden-sm hidden-xs',
|
||||
sourceModel: 'labels',
|
||||
sourceField: 'name'
|
||||
},
|
||||
}
|
||||
};
|
||||
}];
|
||||
@ -1,30 +0,0 @@
|
||||
function JobStrings (BaseString) {
|
||||
BaseString.call(this, 'jobs');
|
||||
|
||||
const { t } = this;
|
||||
const ns = this.jobs;
|
||||
|
||||
ns.state = {
|
||||
LIST_BREADCRUMB_LABEL: t.s('JOBS')
|
||||
};
|
||||
|
||||
ns.list = {
|
||||
PANEL_TITLE: t.s('JOBS'),
|
||||
ADD_BUTTON_LABEL: t.s('ADD'),
|
||||
ADD_DD_JT_LABEL: t.s('Job Template'),
|
||||
ADD_DD_WF_LABEL: t.s('Workflow Template'),
|
||||
ROW_ITEM_LABEL_ACTIVITY: t.s('Activity'),
|
||||
ROW_ITEM_LABEL_INVENTORY: t.s('Inventory'),
|
||||
ROW_ITEM_LABEL_PROJECT: t.s('Project'),
|
||||
ROW_ITEM_LABEL_TEMPLATE: t.s('Template'),
|
||||
ROW_ITEM_LABEL_CREDENTIALS: t.s('Credentials'),
|
||||
ROW_ITEM_LABEL_MODIFIED: t.s('Last Modified'),
|
||||
ROW_ITEM_LABEL_RAN: t.s('Last Ran'),
|
||||
ROW_ITEM_LABEL_STARTED: t.s('Started'),
|
||||
ROW_ITEM_LABEL_FINISHED: t.s('Finished')
|
||||
};
|
||||
}
|
||||
|
||||
JobStrings.$inject = ['BaseStringService'];
|
||||
|
||||
export default JobStrings;
|
||||
@ -15,10 +15,6 @@ import InstanceGroupsListController from './list/instance-groups-list.controller
|
||||
import InstancesTemplate from './instances/instances-list.partial.html';
|
||||
import InstanceListController from './instances/instances.controller';
|
||||
|
||||
import JobsTemplate from './jobs/jobs-list.partial.html';
|
||||
import InstanceGroupJobsListController from './jobs/jobs.controller';
|
||||
import InstanceJobsListController from './instances/instance-jobs/instance-jobs.controller';
|
||||
|
||||
import InstanceModalTemplate from './instances/instance-modal.partial.html';
|
||||
import InstanceModalController from './instances/instance-modal.controller.js';
|
||||
|
||||
@ -26,7 +22,9 @@ import list from './instance-groups.list';
|
||||
import service from './instance-groups.service';
|
||||
|
||||
import InstanceGroupsStrings from './instance-groups.strings';
|
||||
import JobStrings from './jobs/jobs.strings';
|
||||
|
||||
import instanceGroupJobsRoute from '~features/jobs/routes/instanceGroupJobs.route.js';
|
||||
import instanceJobsRoute from '~features/jobs/routes/instanceJobs.route.js';
|
||||
|
||||
const MODULE_NAME = 'instanceGroups';
|
||||
|
||||
@ -255,73 +253,8 @@ function InstanceGroupsRun ($stateExtender, strings, ComponentsStrings) {
|
||||
resolvedModels: InstanceGroupsResolve
|
||||
});
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'instanceGroups.instanceJobs',
|
||||
url: '/:instance_group_id/instances/:instance_id/jobs',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'instanceGroups.instances',
|
||||
label: ComponentsStrings.get('layout.JOBS')
|
||||
},
|
||||
views: {
|
||||
'instanceJobs@instanceGroups': {
|
||||
templateUrl: JobsTemplate,
|
||||
controller: 'InstanceJobsListController',
|
||||
controllerAs: 'vm'
|
||||
},
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '10',
|
||||
order_by: '-finished'
|
||||
},
|
||||
dynamic: true
|
||||
},
|
||||
},
|
||||
data: {
|
||||
socket: {
|
||||
"groups": {
|
||||
"jobs": ["status_changed"],
|
||||
}
|
||||
}
|
||||
},
|
||||
resolvedModels: InstanceGroupsResolve
|
||||
});
|
||||
|
||||
$stateExtender.addState({
|
||||
name: 'instanceGroups.jobs',
|
||||
url: '/:instance_group_id/jobs',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'instanceGroups.edit',
|
||||
label: ComponentsStrings.get('layout.JOBS')
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '10',
|
||||
order_by: '-finished'
|
||||
},
|
||||
dynamic: true
|
||||
}
|
||||
},
|
||||
data: {
|
||||
socket: {
|
||||
"groups": {
|
||||
"jobs": ["status_changed"],
|
||||
}
|
||||
}
|
||||
},
|
||||
views: {
|
||||
'jobs@instanceGroups': {
|
||||
templateUrl: JobsTemplate,
|
||||
controller: 'InstanceGroupJobsListController',
|
||||
controllerAs: 'vm'
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
resolvedModels: InstanceGroupsResolve
|
||||
}
|
||||
});
|
||||
$stateExtender.addState(instanceJobsRoute);
|
||||
$stateExtender.addState(instanceGroupJobsRoute);
|
||||
}
|
||||
|
||||
InstanceGroupsRun.$inject = [
|
||||
@ -334,16 +267,13 @@ angular.module(MODULE_NAME, [])
|
||||
.service('InstanceGroupsService', service)
|
||||
.factory('InstanceGroupList', list)
|
||||
.controller('InstanceGroupsListController', InstanceGroupsListController)
|
||||
.controller('InstanceGroupJobsListController', InstanceGroupJobsListController)
|
||||
.controller('InstanceListController', InstanceListController)
|
||||
.controller('InstanceJobsListController', InstanceJobsListController)
|
||||
.directive('instanceListPolicy', InstanceListPolicy)
|
||||
.directive('instanceGroupsMultiselect', instanceGroupsMultiselect)
|
||||
.directive('instanceGroupsModal', instanceGroupsModal)
|
||||
.directive('capacityAdjuster', CapacityAdjuster)
|
||||
.directive('capacityBar', CapacityBar)
|
||||
.service('InstanceGroupsStrings', InstanceGroupsStrings)
|
||||
.service('JobStrings', JobStrings)
|
||||
.run(InstanceGroupsRun);
|
||||
|
||||
export default MODULE_NAME;
|
||||
|
||||
@ -8,12 +8,11 @@ import adhoc from './adhoc/main';
|
||||
import group from './related/groups/main';
|
||||
import sources from './related/sources/main';
|
||||
import relatedHost from './related/hosts/main';
|
||||
import inventoryCompletedJobs from './related/completed-jobs/main';
|
||||
import inventoryList from './list/main';
|
||||
import InventoryList from './inventory.list';
|
||||
import adHocRoute from './adhoc/adhoc.route';
|
||||
import insights from './insights/main';
|
||||
import completedJobsRoute from './related/completed-jobs/completed-jobs.route';
|
||||
import completedJobsRoute from '~features/jobs/routes/inventoryCompletedJobs.route.js';
|
||||
import inventorySourceEditRoute from './related/sources/edit/sources-edit.route';
|
||||
import inventorySourceEditNotificationsRoute from './related/sources/edit/sources-notifications.route';
|
||||
import inventorySourceAddRoute from './related/sources/add/sources-add.route';
|
||||
@ -53,7 +52,6 @@ angular.module('inventory', [
|
||||
group.name,
|
||||
sources.name,
|
||||
relatedHost.name,
|
||||
inventoryCompletedJobs.name,
|
||||
inventoryList.name,
|
||||
insights.name,
|
||||
SmartInventory.name,
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
|
||||
export default ['i18n', function(i18n) {
|
||||
return {
|
||||
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
|
||||
awToolTip: i18n._('Please save and run a job to view.'),
|
||||
dataPlacement: 'top',
|
||||
name: 'completed_jobs',
|
||||
basePath: 'unified_jobs',
|
||||
iterator: 'completed_job',
|
||||
search: {
|
||||
"or__job__inventory": ''
|
||||
},
|
||||
editTitle: i18n._('COMPLETED JOBS'),
|
||||
index: false,
|
||||
hover: true,
|
||||
well: true,
|
||||
emptyListText: i18n._('No completed jobs'),
|
||||
|
||||
fields: {
|
||||
status: {
|
||||
label: '',
|
||||
columnClass: 'List-staticColumn--smallStatus',
|
||||
awToolTip: "{{ completed_job.status_tip }}",
|
||||
awTipPlacement: "right",
|
||||
dataTitle: "{{ completed_job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ completed_job.status }}',
|
||||
iconOnly: true,
|
||||
uiSref: '{{completed_job.linkToDetails}}',
|
||||
nosort: true
|
||||
},
|
||||
id: {
|
||||
label: i18n._('ID'),
|
||||
uiSref: '{{completed_job.linkToDetails}}',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||
awToolTip: "{{ completed_job.status_tip }}",
|
||||
dataPlacement: 'top'
|
||||
},
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
|
||||
uiSref: '{{completed_job.linkToDetails}}',
|
||||
awToolTip: "{{ completed_job.name | sanitize }}",
|
||||
dataPlacement: 'top'
|
||||
},
|
||||
type: {
|
||||
label: i18n._('Type'),
|
||||
ngBind: 'completed_job.type_label',
|
||||
link: false,
|
||||
columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
|
||||
},
|
||||
finished: {
|
||||
label: i18n._('Finished'),
|
||||
noLink: true,
|
||||
filter: "longDate",
|
||||
columnClass: "col-lg-3 col-md-3 col-sm-3 hidden-xs",
|
||||
key: true,
|
||||
desc: true
|
||||
}
|
||||
},
|
||||
|
||||
actions: { },
|
||||
|
||||
fieldActions: {
|
||||
|
||||
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
|
||||
|
||||
submit: {
|
||||
ngShow: "!completed_job.type == 'system_job' || completed_job.summary_fields.user_capabilities.start",
|
||||
// uses the at-relaunch directive
|
||||
relaunch: true
|
||||
},
|
||||
"delete": {
|
||||
mode: 'all',
|
||||
ngClick: 'deleteJob(completed_job.id)',
|
||||
awToolTip: i18n._('Delete the job'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: 'completed_job.summary_fields.user_capabilities.delete'
|
||||
}
|
||||
}
|
||||
};}];
|
||||
@ -1,60 +0,0 @@
|
||||
import { N_ } from '../../../../i18n';
|
||||
|
||||
export default {
|
||||
url: "/completed_jobs",
|
||||
params: {
|
||||
completed_job_search: {
|
||||
value: {
|
||||
page_size: '20',
|
||||
or__job__inventory:"",
|
||||
or__adhoccommand__inventory:"",
|
||||
or__inventoryupdate__inventory_source__inventory:"",
|
||||
order_by: "-id"
|
||||
},
|
||||
dynamic: true,
|
||||
squash:""
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: N_("COMPLETED JOBS")
|
||||
},
|
||||
views: {
|
||||
'related': {
|
||||
templateProvider: function(FormDefinition, GenerateForm) {
|
||||
let html = GenerateForm.buildCollection({
|
||||
mode: 'edit',
|
||||
related: 'completed_jobs',
|
||||
form: typeof(FormDefinition) === 'function' ?
|
||||
FormDefinition() : FormDefinition
|
||||
});
|
||||
return html;
|
||||
},
|
||||
controller: 'JobsList'
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
ListDefinition: ['InventoryCompletedJobsList', (InventoryCompletedJobsList) => {
|
||||
return InventoryCompletedJobsList;
|
||||
}],
|
||||
Dataset: ['InventoryCompletedJobsList', 'QuerySet', '$stateParams', 'GetBasePath', '$interpolate', '$rootScope',
|
||||
(list, qs, $stateParams, GetBasePath, $interpolate, $rootScope) => {
|
||||
// allow related list definitions to use interpolated $rootScope / $stateParams in basePath field
|
||||
let path, interpolator;
|
||||
if (GetBasePath(list.basePath)) {
|
||||
path = GetBasePath(list.basePath);
|
||||
} else {
|
||||
interpolator = $interpolate(list.basePath);
|
||||
path = interpolator({ $rootScope: $rootScope, $stateParams: $stateParams });
|
||||
}
|
||||
|
||||
let inventory_id = $stateParams.inventory_id ? $stateParams.inventory_id : $stateParams.smartinventory_id;
|
||||
|
||||
$stateParams[`${list.iterator}_search`].or__job__inventory = inventory_id;
|
||||
$stateParams[`${list.iterator}_search`].or__adhoccommand__inventory = inventory_id;
|
||||
$stateParams[`${list.iterator}_search`].or__inventoryupdate__inventory_source__inventory = inventory_id;
|
||||
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@ -1,11 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2017 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import list from './completed-jobs.list';
|
||||
|
||||
export default
|
||||
angular.module('inventoryCompletedJobs', [])
|
||||
.factory('InventoryCompletedJobsList', list);
|
||||
@ -4,25 +4,7 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default ['i18n', 'InventoryCompletedJobsList', function(i18n, InventoryCompletedJobsList) {
|
||||
|
||||
var completed_jobs_object = {
|
||||
name: 'completed_jobs',
|
||||
index: false,
|
||||
basePath: "unified_jobs",
|
||||
title: i18n._('Completed Jobs'),
|
||||
iterator: 'completed_job',
|
||||
generateList: true,
|
||||
skipGenerator: true,
|
||||
search: {
|
||||
"or__job__inventory": ''
|
||||
},
|
||||
ngClick: "$state.go('inventories.editSmartInventory.completed_jobs')"
|
||||
};
|
||||
|
||||
let clone = _.clone(InventoryCompletedJobsList);
|
||||
completed_jobs_object = angular.extend(clone, completed_jobs_object);
|
||||
|
||||
export default ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
addTitle: i18n._('NEW SMART INVENTORY'),
|
||||
@ -169,7 +151,11 @@ export default ['i18n', 'InventoryCompletedJobsList', function(i18n, InventoryCo
|
||||
ngClick: "$state.go('inventories.editSmartInventory.hosts');",
|
||||
skipGenerator: true
|
||||
},
|
||||
completed_jobs: completed_jobs_object
|
||||
completed_jobs: {
|
||||
title: i18n._('Completed Jobs'),
|
||||
skipGenerator: true,
|
||||
ngClick: "$state.go('inventories.editSmartInventory.completed_jobs')"
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@ -10,25 +10,8 @@
|
||||
* @description This form is for adding/editing an inventory
|
||||
*/
|
||||
|
||||
export default ['i18n', 'InventoryCompletedJobsList',
|
||||
function(i18n, InventoryCompletedJobsList) {
|
||||
|
||||
var completed_jobs_object = {
|
||||
name: 'completed_jobs',
|
||||
index: false,
|
||||
basePath: "unified_jobs",
|
||||
include: "InventoryCompletedJobsList",
|
||||
title: i18n._('Completed Jobs'),
|
||||
iterator: 'completed_job',
|
||||
generateList: true,
|
||||
skipGenerator: true,
|
||||
search: {
|
||||
"or__job__inventory": ''
|
||||
}
|
||||
};
|
||||
let clone = _.clone(InventoryCompletedJobsList);
|
||||
completed_jobs_object = angular.extend(clone, completed_jobs_object);
|
||||
|
||||
export default ['i18n',
|
||||
function(i18n) {
|
||||
return {
|
||||
|
||||
addTitle: i18n._('NEW INVENTORY'),
|
||||
@ -185,7 +168,10 @@ function(i18n, InventoryCompletedJobsList) {
|
||||
iterator: 'inventory_source',
|
||||
skipGenerator: true
|
||||
},
|
||||
completed_jobs: completed_jobs_object
|
||||
completed_jobs: {
|
||||
title: i18n._('Completed Jobs'),
|
||||
skipGenerator: true
|
||||
}
|
||||
},
|
||||
relatedButtons: {
|
||||
remediate_inventory: {
|
||||
|
||||
@ -1,115 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
|
||||
export default ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
name: 'jobs',
|
||||
basePath: 'unified_jobs',
|
||||
iterator: 'job',
|
||||
editTitle: i18n._('ALL JOBS'),
|
||||
index: false,
|
||||
hover: true,
|
||||
well: false,
|
||||
emptyListText: i18n._('No jobs have yet run.'),
|
||||
title: false,
|
||||
|
||||
fields: {
|
||||
status: {
|
||||
label: '',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus',
|
||||
dataTipWatch: 'job.status_tip',
|
||||
awToolTip: "{{ job.status_tip }}",
|
||||
awTipPlacement: "right",
|
||||
dataTitle: "{{ job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ job.status }}',
|
||||
iconOnly: true,
|
||||
uiSref: '{{job.linkToDetails}}',
|
||||
nosort: true
|
||||
},
|
||||
id: {
|
||||
label: 'ID',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||
awToolTip: "{{ job.status_tip }}",
|
||||
dataPlacement: 'top',
|
||||
noLink: true
|
||||
},
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
|
||||
uiSref: '{{job.linkToDetails}}',
|
||||
badgePlacement: 'right',
|
||||
badgeCustom: true,
|
||||
badgeIcon: `<a href="{{ job.workflow_result_link }}"
|
||||
aw-tool-tip="{{'View workflow results'|translate}}"
|
||||
data-placement="top"
|
||||
data-original-title="" title="">
|
||||
<i class="WorkflowBadge"
|
||||
ng-show="job.launch_type === 'workflow' ">
|
||||
W
|
||||
</i>
|
||||
</a>`
|
||||
},
|
||||
type: {
|
||||
label: i18n._('Type'),
|
||||
ngBind: 'job.type_label',
|
||||
link: false,
|
||||
columnClass: "col-lg-2 hidden-md hidden-sm hidden-xs",
|
||||
columnShow: "showJobType",
|
||||
},
|
||||
finished: {
|
||||
label: i18n._('Finished'),
|
||||
noLink: true,
|
||||
filter: "longDate",
|
||||
columnClass: "col-lg-2 col-md-3 col-sm-3 hidden-xs",
|
||||
key: true,
|
||||
desc: true
|
||||
},
|
||||
labels: {
|
||||
label: i18n._('Labels'),
|
||||
type: 'labels',
|
||||
nosort: true,
|
||||
showDelete: false,
|
||||
columnClass: 'List-tableCell col-lg-4 col-md-4 hidden-sm hidden-xs',
|
||||
sourceModel: 'labels',
|
||||
sourceField: 'name'
|
||||
},
|
||||
},
|
||||
|
||||
actions: { },
|
||||
|
||||
fieldActions: {
|
||||
|
||||
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
|
||||
"view": {
|
||||
mode: "all",
|
||||
ngClick: "viewjobResults(job)",
|
||||
awToolTip: i18n._("View the job"),
|
||||
dataPlacement: "top"
|
||||
},
|
||||
submit: {
|
||||
ngShow: "!(job.type == 'system_job') && job.summary_fields.user_capabilities.start",
|
||||
// uses the at-relaunch directive
|
||||
relaunch: true,
|
||||
},
|
||||
cancel: {
|
||||
mode: 'all',
|
||||
ngClick: 'deleteJob(job.id)',
|
||||
awToolTip: i18n._('Cancel the job'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: "(job.status === 'running'|| job.status === 'waiting' || job.status === 'pending') && job.summary_fields.user_capabilities.start"
|
||||
},
|
||||
"delete": {
|
||||
mode: 'all',
|
||||
ngClick: 'deleteJob(job.id)',
|
||||
awToolTip: i18n._('Delete the job'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: "(job.status !== 'running' && job.status !== 'waiting' && job.status !== 'pending') && job.summary_fields.user_capabilities.delete"
|
||||
}
|
||||
}
|
||||
};
|
||||
}];
|
||||
@ -1,145 +0,0 @@
|
||||
export default
|
||||
function DeleteJob($state, Find, Rest, Wait, ProcessErrors, Prompt, Alert,
|
||||
$filter, i18n) {
|
||||
return function(params) {
|
||||
var scope = params.scope,
|
||||
id = params.id,
|
||||
job = params.job,
|
||||
callback = params.callback,
|
||||
action, jobs, url, action_label, hdr;
|
||||
|
||||
if (!job) {
|
||||
if (scope.completed_jobs) {
|
||||
jobs = scope.completed_jobs;
|
||||
}
|
||||
else if (scope.running_jobs) {
|
||||
jobs = scope.running_jobs;
|
||||
}
|
||||
else if (scope.queued_jobs) {
|
||||
jobs = scope.queued_jobs;
|
||||
}
|
||||
else if (scope.all_jobs) {
|
||||
jobs = scope.all_jobs;
|
||||
}
|
||||
else if (scope.jobs) {
|
||||
jobs = scope.jobs;
|
||||
}
|
||||
job = Find({list: jobs, key: 'id', val: id });
|
||||
}
|
||||
|
||||
if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') {
|
||||
url = job.related.cancel;
|
||||
action_label = 'cancel';
|
||||
hdr = i18n._('Cancel');
|
||||
} else {
|
||||
url = job.url;
|
||||
action_label = 'delete';
|
||||
hdr = i18n._('Delete');
|
||||
}
|
||||
|
||||
action = function () {
|
||||
Wait('start');
|
||||
Rest.setUrl(url);
|
||||
if (action_label === 'cancel') {
|
||||
Rest.post()
|
||||
.then(() => {
|
||||
$('#prompt-modal').modal('hide');
|
||||
if (callback) {
|
||||
scope.$emit(callback, action_label);
|
||||
}
|
||||
else {
|
||||
$state.reload();
|
||||
Wait('stop');
|
||||
}
|
||||
})
|
||||
.catch(({obj, status}) => {
|
||||
Wait('stop');
|
||||
$('#prompt-modal').modal('hide');
|
||||
if (status === 403) {
|
||||
Alert('Error', obj.detail);
|
||||
}
|
||||
// Ignore the error. The job most likely already finished.
|
||||
// ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
||||
// ' failed. POST returned status: ' + status });
|
||||
});
|
||||
} else {
|
||||
Rest.destroy()
|
||||
.then(() => {
|
||||
$('#prompt-modal').modal('hide');
|
||||
if (callback) {
|
||||
scope.$emit(callback, action_label);
|
||||
}
|
||||
else {
|
||||
let reloadListStateParams = null;
|
||||
|
||||
if(scope.jobs.length === 1 && $state.params.job_search && !_.isEmpty($state.params.job_search.page) && $state.params.job_search.page !== '1') {
|
||||
reloadListStateParams = _.cloneDeep($state.params);
|
||||
reloadListStateParams.job_search.page = (parseInt(reloadListStateParams.job_search.page)-1).toString();
|
||||
}
|
||||
|
||||
$state.go('.', reloadListStateParams, {reload: true});
|
||||
Wait('stop');
|
||||
}
|
||||
})
|
||||
.catch(({obj, status}) => {
|
||||
Wait('stop');
|
||||
$('#prompt-modal').modal('hide');
|
||||
if (status === 403) {
|
||||
Alert('Error', obj.detail);
|
||||
}
|
||||
// Ignore the error. The job most likely already finished.
|
||||
//ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
||||
// ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (scope.removeCancelNotAllowed) {
|
||||
scope.removeCancelNotAllowed();
|
||||
}
|
||||
scope.removeCancelNotAllowed = scope.$on('CancelNotAllowed', function() {
|
||||
Wait('stop');
|
||||
Alert('Job Completed', 'The request to cancel the job could not be submitted. The job already completed.', 'alert-info');
|
||||
});
|
||||
|
||||
if (scope.removeCancelJob) {
|
||||
scope.removeCancelJob();
|
||||
}
|
||||
scope.removeCancelJob = scope.$on('CancelJob', function() {
|
||||
var cancelBody = "<div class=\"Prompt-bodyQuery\">" + i18n._("Are you sure you want to submit the request to cancel this job?") + "</div>";
|
||||
var deleteBody = "<div class=\"Prompt-bodyQuery\">" + i18n._("Are you sure you want to delete this job?") + "</div>";
|
||||
Prompt({
|
||||
hdr: hdr,
|
||||
resourceName: `#${job.id} ` + $filter('sanitize')(job.name),
|
||||
body: (action_label === 'cancel' || job.status === 'new') ? cancelBody : deleteBody,
|
||||
action: action,
|
||||
actionText: (action_label === 'cancel' || job.status === 'new') ? i18n._("OK") : i18n._("DELETE")
|
||||
});
|
||||
});
|
||||
|
||||
if (action_label === 'cancel') {
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.then(({data}) => {
|
||||
if (data.can_cancel) {
|
||||
scope.$emit('CancelJob');
|
||||
}
|
||||
else {
|
||||
scope.$emit('CancelNotAllowed');
|
||||
}
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
||||
' failed. GET returned: ' + status });
|
||||
});
|
||||
}
|
||||
else {
|
||||
scope.$emit('CancelJob');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
DeleteJob.$inject =
|
||||
[ '$state', 'Find', 'Rest', 'Wait',
|
||||
'ProcessErrors', 'Prompt', 'Alert', '$filter', 'i18n'
|
||||
];
|
||||
@ -1,143 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Jobs
|
||||
* @description This controller's for the jobs page
|
||||
*/
|
||||
|
||||
export default ['$state', '$rootScope', '$scope', '$stateParams', 'Find', 'DeleteJob',
|
||||
'GetBasePath', 'Dataset', 'QuerySet', 'ListDefinition', '$interpolate',
|
||||
function($state, $rootScope, $scope, $stateParams, Find, DeleteJob,
|
||||
GetBasePath, Dataset, qs, ListDefinition, $interpolate) {
|
||||
|
||||
var list = ListDefinition;
|
||||
|
||||
init();
|
||||
|
||||
function init() {
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
$scope.showJobType = true;
|
||||
}
|
||||
|
||||
$scope.$on(`${list.iterator}_options`, function(event, data){
|
||||
$scope.options = data.data.actions.GET;
|
||||
optionsRequestDataProcessing();
|
||||
});
|
||||
|
||||
$scope.$watchCollection(`${$scope.list.name}`, function() {
|
||||
optionsRequestDataProcessing();
|
||||
}
|
||||
);
|
||||
|
||||
// iterate over the list and add fields like type label, after the
|
||||
// OPTIONS request returns, or the list is sorted/paginated/searched
|
||||
function optionsRequestDataProcessing(){
|
||||
|
||||
if($scope[list.name] && $scope[list.name].length > 0) {
|
||||
$scope[list.name].forEach(function(item, item_idx) {
|
||||
var itm = $scope[list.name][item_idx];
|
||||
|
||||
switch (item.type) {
|
||||
case 'job':
|
||||
item.linkToDetails = `jobResult({id: ${item.id}})`;
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
item.linkToDetails = `adHocJobStdout({id: ${item.id}})`;
|
||||
break;
|
||||
case 'system_job':
|
||||
item.linkToDetails = `managementJobStdout({id: ${item.id}})`;
|
||||
break;
|
||||
case 'project_update':
|
||||
item.linkToDetails = `scmUpdateStdout({id: ${item.id}})`;
|
||||
break;
|
||||
case 'inventory_update':
|
||||
item.linkToDetails = `inventorySyncStdout({id: ${item.id}})`;
|
||||
break;
|
||||
case 'workflow_job':
|
||||
item.linkToDetails = `workflowResults({id: ${item.id}})`;
|
||||
break;
|
||||
}
|
||||
|
||||
if(item.summary_fields && item.summary_fields.source_workflow_job &&
|
||||
item.summary_fields.source_workflow_job.id){
|
||||
item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`;
|
||||
}
|
||||
|
||||
// Set the item type label
|
||||
if (list.fields.type && $scope.options &&
|
||||
$scope.options.hasOwnProperty('type')) {
|
||||
$scope.options.type.choices.forEach(function(choice) {
|
||||
if (choice[0] === item.type) {
|
||||
itm.type_label = choice[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
buildTooltips(itm);
|
||||
});
|
||||
}
|
||||
}
|
||||
function buildTooltips(job) {
|
||||
job.status_tip = 'Job ' + job.status + ". Click for details.";
|
||||
}
|
||||
|
||||
$scope.deleteJob = function(id) {
|
||||
DeleteJob({ scope: $scope, id: id });
|
||||
};
|
||||
|
||||
$scope.viewjobResults = function(job) {
|
||||
|
||||
var goTojobResults = function(state) {
|
||||
$state.go(state, { id: job.id }, { reload: true });
|
||||
};
|
||||
switch (job.type) {
|
||||
case 'job':
|
||||
goTojobResults('jobResult');
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
goTojobResults('adHocJobStdout');
|
||||
break;
|
||||
case 'system_job':
|
||||
goTojobResults('managementJobStdout');
|
||||
break;
|
||||
case 'project_update':
|
||||
goTojobResults('scmUpdateStdout');
|
||||
break;
|
||||
case 'inventory_update':
|
||||
goTojobResults('inventorySyncStdout');
|
||||
break;
|
||||
case 'workflow_job':
|
||||
goTojobResults('workflowResults');
|
||||
break;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.$on('ws-jobs', function(){
|
||||
let path;
|
||||
if (GetBasePath(list.basePath) || GetBasePath(list.name)) {
|
||||
path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
} else {
|
||||
// completed jobs base path involves $stateParams
|
||||
let interpolator = $interpolate(list.basePath);
|
||||
path = interpolator({ $rootScope: $rootScope, $stateParams: $stateParams });
|
||||
}
|
||||
qs.search(path, $state.params[`${list.iterator}_search`])
|
||||
.then(function(searchResponse) {
|
||||
$scope[`${list.iterator}_dataset`] = searchResponse.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('ws-schedules', function(){
|
||||
$state.reload();
|
||||
});
|
||||
}];
|
||||
@ -1,23 +0,0 @@
|
||||
<div class="tab-pane" id="jobs-page">
|
||||
<div ng-cloak id="htmlTemplate" class="Panel">
|
||||
<div>
|
||||
<div id="jobs_tabs" class="Form-tabHolder">
|
||||
<div class="Form-tabs">
|
||||
<div class="Form-tab" id="active_jobs_link" ng-class="{'is-selected': $state.is('jobs')}" ng-click="$state.go('jobs')">
|
||||
<translate>Jobs</translate>
|
||||
</div>
|
||||
<div id="scheduled_jobs_link" class="Form-tab" ng-class="{'is-selected': $state.is('jobs.schedules')}" ng-click="$state.go('.schedules')">
|
||||
<translate>Schedules</translate>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Form-tabActions">
|
||||
<button id="refresh_btn" ng-show="socketStatus === 'error'" aw-tool-tip="Refresh the page" data-placement="top" class="btn List-buttonDefault" ng-click="refreshJobs()" toolbar="true">
|
||||
<span><translate>REFRESH</translate></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div ui-view="list"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-include="'/static/partials/logviewer.html'"></div>
|
||||
</div>
|
||||
@ -1,59 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import { N_ } from '../i18n';
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
searchPrefix: 'job',
|
||||
name: 'jobs',
|
||||
url: '/jobs',
|
||||
ncyBreadcrumb: {
|
||||
label: N_("JOBS")
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
not__launch_type: 'sync',
|
||||
order_by: '-finished'
|
||||
},
|
||||
dynamic: true,
|
||||
squash: false
|
||||
}
|
||||
},
|
||||
data: {
|
||||
socket: {
|
||||
"groups": {
|
||||
"jobs": ["status_changed"],
|
||||
"schedules": ["changed"]
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['AllJobsList', 'QuerySet', '$stateParams', 'GetBasePath', (list, qs, $stateParams, GetBasePath) => {
|
||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}],
|
||||
ListDefinition: ['AllJobsList', (list) => {
|
||||
return list;
|
||||
}]
|
||||
},
|
||||
views: {
|
||||
'@': {
|
||||
templateUrl: templateUrl('jobs/jobs')
|
||||
},
|
||||
'list@jobs': {
|
||||
templateProvider: function(AllJobsList, generateList) {
|
||||
let html = generateList.build({
|
||||
list: AllJobsList,
|
||||
mode: 'edit'
|
||||
});
|
||||
return html;
|
||||
},
|
||||
controller: 'JobsList'
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,19 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2017 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import jobsList from './jobs-list.controller';
|
||||
import jobsRoute from './jobs.route';
|
||||
import DeleteJob from './factories/delete-job.factory';
|
||||
import AllJobsList from './all-jobs.list';
|
||||
|
||||
export default
|
||||
angular.module('JobsModule', [])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(jobsRoute);
|
||||
}])
|
||||
.controller('JobsList', jobsList)
|
||||
.factory('DeleteJob', DeleteJob)
|
||||
.factory('AllJobsList', AllJobsList);
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
import managementJobsCard from './card/main';
|
||||
import managementJobsScheduler from './scheduler/main';
|
||||
import list from './management-jobs.list';
|
||||
import managementJobsNotifications from './notifications/main';
|
||||
|
||||
export default
|
||||
@ -14,5 +13,4 @@ export default
|
||||
managementJobsCard.name,
|
||||
managementJobsScheduler.name,
|
||||
managementJobsNotifications.name
|
||||
])
|
||||
.factory('managementJobsListObject', list);
|
||||
]);
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function(){
|
||||
return {
|
||||
name: 'configure_jobs',
|
||||
iterator: 'configure_job',
|
||||
index: false,
|
||||
hover: true,
|
||||
listTitle: 'MANAGEMENT JOBS',
|
||||
|
||||
fields: {
|
||||
name: {
|
||||
label: 'Name',
|
||||
columnClass: 'col-sm-4 col-xs-4',
|
||||
awToolTip: '{{configure_job.description | sanitize}}',
|
||||
dataPlacement: 'top'
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
},
|
||||
fieldActions: {
|
||||
submit: {
|
||||
label: 'Launch',
|
||||
mode: 'all',
|
||||
ngClick: 'submitJob(configure_job.id, configure_job.name)',
|
||||
awToolTip: 'Start a job using this template',
|
||||
dataPlacement: 'top'
|
||||
},
|
||||
schedule: {
|
||||
label: 'Schedule',
|
||||
mode: 'all',
|
||||
ngClick: 'configureSchedule()',
|
||||
awToolTip: 'Schedule job template runs',
|
||||
dataPlacement: 'top',
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
import { PortalModeJobsController } from '../portal-mode-jobs.controller';
|
||||
|
||||
// Using multiple named views requires a parent layout
|
||||
// https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
|
||||
export default {
|
||||
name: 'portalMode.allJobs',
|
||||
url: '/alljobs?{job_search:queryset}',
|
||||
ncyBreadcrumb: {
|
||||
skip: true
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '20',
|
||||
order_by: '-finished'
|
||||
},
|
||||
dynamic: true
|
||||
}
|
||||
},
|
||||
data: {
|
||||
socket: {
|
||||
"groups": {
|
||||
"jobs": ["status_changed"]
|
||||
}
|
||||
}
|
||||
},
|
||||
views: {
|
||||
'jobs@portalMode': {
|
||||
templateProvider: function(PortalJobsList, generateList) {
|
||||
let html = generateList.build({
|
||||
list: PortalJobsList,
|
||||
mode: 'edit'
|
||||
});
|
||||
return html;
|
||||
},
|
||||
controller: PortalModeJobsController
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
jobsDataset: ['PortalJobsList', 'QuerySet', '$rootScope', '$stateParams', 'GetBasePath',
|
||||
function(list, qs, $rootScope, $stateParams, GetBasePath) {
|
||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
return $rootScope.loginConfig.promise.then(() => {
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
});
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@ -1,51 +0,0 @@
|
||||
import { PortalModeJobsController } from '../portal-mode-jobs.controller';
|
||||
|
||||
// Using multiple named views requires a parent layout
|
||||
// https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
|
||||
export default {
|
||||
name: 'portalMode.myJobs',
|
||||
url: '/myjobs?{job_search:queryset}',
|
||||
ncyBreadcrumb: {
|
||||
skip: true
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '20',
|
||||
order_by: '-finished',
|
||||
created_by: null
|
||||
},
|
||||
dynamic: true
|
||||
}
|
||||
},
|
||||
data: {
|
||||
socket: {
|
||||
"groups": {
|
||||
"jobs": ["status_changed"]
|
||||
}
|
||||
}
|
||||
},
|
||||
views: {
|
||||
'jobs@portalMode': {
|
||||
templateProvider: function(PortalJobsList, generateList) {
|
||||
let html = generateList.build({
|
||||
list: PortalJobsList,
|
||||
mode: 'edit'
|
||||
});
|
||||
return html;
|
||||
},
|
||||
controller: PortalModeJobsController
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
jobsDataset: ['PortalJobsList', 'QuerySet', '$rootScope', '$stateParams', 'GetBasePath',
|
||||
function(list, qs, $rootScope, $stateParams, GetBasePath) {
|
||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
return $rootScope.loginConfig.promise.then(() => {
|
||||
$stateParams[`${list.iterator}_search`].created_by = $rootScope.current_user.id;
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
});
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@ -5,17 +5,15 @@
|
||||
*************************************************/
|
||||
|
||||
import route from './portal-mode.route';
|
||||
import myJobsRoute from './jobs/portal-mode-my-jobs.route';
|
||||
import allJobsRoute from './jobs/portal-mode-all-jobs.route';
|
||||
import PortalJobsList from './portal-jobs.list';
|
||||
import myJobsRoute from '~features/jobs/routes/portalModeMyJobs.route.js';
|
||||
import allJobsRoute from '~features/jobs/routes/portalModeAllJobs.route.js';
|
||||
import PortalJobTemplateList from './portal-job-templates.list';
|
||||
|
||||
export default
|
||||
angular.module('portalMode', [])
|
||||
.factory('PortalJobsList', PortalJobsList)
|
||||
.factory('PortalJobTemplateList', PortalJobTemplateList)
|
||||
.run(['$stateExtender', function($stateExtender){
|
||||
$stateExtender.addState(route);
|
||||
$stateExtender.addState(myJobsRoute);
|
||||
$stateExtender.addState(allJobsRoute);
|
||||
$stateExtender.addState(route);
|
||||
$stateExtender.addState(myJobsRoute);
|
||||
$stateExtender.addState(allJobsRoute);
|
||||
}]);
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
|
||||
export default ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
name: 'jobs',
|
||||
iterator: 'job',
|
||||
editTitle: i18n._('JOBS'),
|
||||
index: false,
|
||||
hover: true,
|
||||
well: true,
|
||||
listTitle: i18n._('JOBS'),
|
||||
emptyListText: i18n._('There are no jobs to display at this time'),
|
||||
searchBarFullWidth: true,
|
||||
|
||||
fields: {
|
||||
status: {
|
||||
label: '',
|
||||
columnClass: 'List-staticColumn--smallStatus',
|
||||
dataTitle: "{{ job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ job.status }}',
|
||||
iconOnly: true,
|
||||
nosort: true,
|
||||
awTipPlacement: "top",
|
||||
awToolTip: "{{ job.status_tip }}",
|
||||
dataTipWatch: 'job.status_tip',
|
||||
ngClick:"viewjobResults(job)",
|
||||
},
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6 List-staticColumnAdjacent',
|
||||
linkTo: '/#/jobs/{{job.id}}',
|
||||
},
|
||||
finished: {
|
||||
label: i18n._('Finished'),
|
||||
noLink: true,
|
||||
filter: "longDate",
|
||||
key: true,
|
||||
desc: true,
|
||||
columnClass: "col-lg-4 col-md-4 col-sm-3"
|
||||
}
|
||||
},
|
||||
|
||||
actions: { }
|
||||
};}];
|
||||
@ -1,105 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export function PortalModeJobsController($scope, $state, qs, GetBasePath, PortalJobsList, Dataset) {
|
||||
|
||||
var list = PortalJobsList;
|
||||
|
||||
$scope.$on('ws-jobs', function() {
|
||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
qs.search(path, $state.params[`${list.iterator}_search`])
|
||||
.then(function(searchResponse) {
|
||||
$scope[`${list.iterator}_dataset`] = searchResponse.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
});
|
||||
});
|
||||
|
||||
init();
|
||||
|
||||
function init(data) {
|
||||
let d = (!data) ? Dataset : data;
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = d.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
$scope.iterator = list.iterator;
|
||||
}
|
||||
|
||||
$scope.refresh = function() {
|
||||
$state.go('.', null, {reload: true});
|
||||
};
|
||||
|
||||
$scope.$on(`${list.iterator}_options`, function(event, data){
|
||||
$scope.options = data.data.actions.GET;
|
||||
optionsRequestDataProcessing();
|
||||
});
|
||||
|
||||
$scope.$watchCollection(`${$scope.list.name}`, function() {
|
||||
optionsRequestDataProcessing();
|
||||
}
|
||||
);
|
||||
|
||||
// iterate over the list and add fields like type label, after the
|
||||
// OPTIONS request returns, or the list is sorted/paginated/searched
|
||||
function optionsRequestDataProcessing(){
|
||||
|
||||
if($scope[list.name] && $scope[list.name].length > 0) {
|
||||
$scope[list.name].forEach(function(item, item_idx) {
|
||||
var itm = $scope[list.name][item_idx];
|
||||
|
||||
if(item.summary_fields && item.summary_fields.source_workflow_job &&
|
||||
item.summary_fields.source_workflow_job.id){
|
||||
item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`;
|
||||
}
|
||||
|
||||
// Set the item type label
|
||||
if (list.fields.type && $scope.options &&
|
||||
$scope.options.hasOwnProperty('type')) {
|
||||
$scope.options.type.choices.forEach(function(choice) {
|
||||
if (choice[0] === item.type) {
|
||||
itm.type_label = choice[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
buildTooltips(itm);
|
||||
});
|
||||
}
|
||||
}
|
||||
function buildTooltips(job) {
|
||||
job.status_tip = `Job ${job.status}. Click for details.`;
|
||||
}
|
||||
|
||||
$scope.viewjobResults = function(job) {
|
||||
|
||||
var goTojobResults = function(state) {
|
||||
$state.go(state, { id: job.id }, { reload: true });
|
||||
};
|
||||
switch (job.type) {
|
||||
case 'job':
|
||||
goTojobResults('jobResult');
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
goTojobResults('adHocJobStdout');
|
||||
break;
|
||||
case 'system_job':
|
||||
goTojobResults('managementJobStdout');
|
||||
break;
|
||||
case 'project_update':
|
||||
goTojobResults('scmUpdateStdout');
|
||||
break;
|
||||
case 'inventory_update':
|
||||
goTojobResults('inventorySyncStdout');
|
||||
break;
|
||||
case 'workflow_job':
|
||||
goTojobResults('workflowResults');
|
||||
break;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
PortalModeJobsController.$inject = ['$scope', '$state', 'QuerySet', 'GetBasePath', 'PortalJobsList', 'jobsDataset'];
|
||||
@ -1,26 +0,0 @@
|
||||
<div id="portal-container-jobs" class="Panel">
|
||||
<div class="FormToggle-container">
|
||||
<div class="btn-group">
|
||||
<button ng-class="{'btn-primary': activeFilter === 'user', 'Button-primary--hollow': activeFilter !== 'user'}"
|
||||
ng-click='filterUser()'
|
||||
class="btn btn-xs"
|
||||
translate>
|
||||
My Jobs
|
||||
</button>
|
||||
<button ng-class="{'btn-primary': activeFilter === 'all', 'Button-primary--hollow': activeFilter !== 'all'}"
|
||||
ng-click='filterAll()'
|
||||
class="btn btn-xs"
|
||||
translate>
|
||||
All Jobs
|
||||
</button>
|
||||
</div>
|
||||
<div class="PortalMode-refresh">
|
||||
<button id="refresh_btn" aw-tool-tip="Refresh the page" data-placement="top" class="btn List-buttonDefault"
|
||||
ng-click="refresh()" ng-show="socketStatus == 'error'">
|
||||
<span translate>REFRESH</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="portal-jobs">
|
||||
</div>
|
||||
</div>
|
||||
@ -349,7 +349,7 @@ export default
|
||||
}]
|
||||
},
|
||||
views: {
|
||||
'list@jobs': {
|
||||
'schedulesList@jobs': {
|
||||
templateProvider: function(ScheduleList, generateList){
|
||||
let html = generateList.build({
|
||||
list: ScheduleList,
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
|
||||
export default ['i18n', function(i18n) {
|
||||
return {
|
||||
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
|
||||
awToolTip: i18n._('Please save and run a job to view.'),
|
||||
dataPlacement: 'top',
|
||||
name: 'completed_jobs',
|
||||
basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/jobs',
|
||||
search: {
|
||||
or__status__in: "successful,failed,error,canceled",
|
||||
order_by: "-id"
|
||||
},
|
||||
iterator: 'completed_job',
|
||||
editTitle: i18n._('COMPLETED JOBS'),
|
||||
index: false,
|
||||
hover: true,
|
||||
well: false,
|
||||
emptyListText: i18n._('No completed jobs'),
|
||||
|
||||
fields: {
|
||||
status: {
|
||||
label: '',
|
||||
columnClass: 'List-staticColumn--smallStatus',
|
||||
awToolTip: "{{ completed_job.status_tip }}",
|
||||
awTipPlacement: "right",
|
||||
dataTitle: "{{ completed_job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ completed_job.status }}',
|
||||
iconOnly: true,
|
||||
uiSref: '{{completed_job.linkToDetails}}',
|
||||
nosort: true
|
||||
},
|
||||
id: {
|
||||
label: 'ID',
|
||||
uiSref: '{{completed_job.linkToDetails}}',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||
awToolTip: "{{ completed_job.status_tip }}",
|
||||
dataPlacement: 'top'
|
||||
},
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
|
||||
uiSref: '{{completed_job.linkToDetails}}',
|
||||
awToolTip: "{{ completed_job.name | sanitize }}",
|
||||
dataPlacement: 'top'
|
||||
},
|
||||
type: {
|
||||
label: i18n._('Type'),
|
||||
ngBind: 'completed_job.type_label',
|
||||
link: false,
|
||||
columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
|
||||
},
|
||||
finished: {
|
||||
label: i18n._('Finished'),
|
||||
noLink: true,
|
||||
filter: "longDate",
|
||||
columnClass: "col-lg-3 col-md-3 col-sm-3 hidden-xs",
|
||||
key: true,
|
||||
desc: true
|
||||
}
|
||||
},
|
||||
|
||||
actions: { },
|
||||
|
||||
fieldActions: {
|
||||
|
||||
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
|
||||
|
||||
submit: {
|
||||
ngShow: "!completed_job.type == 'system_job' || completed_job.summary_fields.user_capabilities.start",
|
||||
// uses the at-relaunch directive
|
||||
relaunch: true
|
||||
},
|
||||
"delete": {
|
||||
mode: 'all',
|
||||
ngClick: 'deleteJob(completed_job.id)',
|
||||
awToolTip: i18n._('Delete the job'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: 'completed_job.summary_fields.user_capabilities.delete'
|
||||
}
|
||||
}
|
||||
};}];
|
||||
@ -11,8 +11,8 @@
|
||||
*/
|
||||
|
||||
|
||||
export default ['NotificationsList', 'CompletedJobsList', 'i18n',
|
||||
function(NotificationsList, CompletedJobsList, i18n) {
|
||||
export default ['NotificationsList', 'i18n',
|
||||
function(NotificationsList, i18n) {
|
||||
return function() {
|
||||
var JobTemplateFormObject = {
|
||||
|
||||
@ -433,7 +433,9 @@ function(NotificationsList, CompletedJobsList, i18n) {
|
||||
include: "NotificationsList"
|
||||
},
|
||||
"completed_jobs": {
|
||||
include: "CompletedJobsList"
|
||||
title: i18n._('Completed Jobs'),
|
||||
skipGenerator: true,
|
||||
ngClick: "$state.go('templates.editJobTemplate.completed_jobs')"
|
||||
}
|
||||
},
|
||||
|
||||
@ -473,11 +475,6 @@ function(NotificationsList, CompletedJobsList, i18n) {
|
||||
JobTemplateFormObject.related[itm].ngClick = "$state.go('templates.editJobTemplate.notifications')";
|
||||
JobTemplateFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
|
||||
}
|
||||
if (JobTemplateFormObject.related[itm].include === "CompletedJobsList") {
|
||||
JobTemplateFormObject.related[itm] = CompletedJobsList;
|
||||
JobTemplateFormObject.related[itm].ngClick = "$state.go('templates.editJobTemplate.completed_jobs')";
|
||||
JobTemplateFormObject.related[itm].generateList = true;
|
||||
}
|
||||
}
|
||||
|
||||
return JobTemplateFormObject;
|
||||
|
||||
@ -95,6 +95,11 @@ export default
|
||||
if (scope.$parent.$parent.template) {
|
||||
scope.labels = scope.$parent.$parent.template.summary_fields.labels.results.slice(0, 5);
|
||||
scope.count = scope.$parent.$parent.template.summary_fields.labels.count;
|
||||
} else if (scope.$parent.$parent.job) {
|
||||
if (_.has(scope, '$parent.$parent.job.summary_fields.labels.results')) {
|
||||
scope.labels = scope.$parent.$parent.job.summary_fields.labels.results.slice(0, 5);
|
||||
scope.count = scope.$parent.$parent.job.summary_fields.labels.count;
|
||||
}
|
||||
} else {
|
||||
scope.$watchCollection(scope.$parent.list.iterator, function() {
|
||||
// To keep the array of labels fresh, we need to set up a watcher - otherwise, the
|
||||
|
||||
@ -16,11 +16,11 @@ import workflowMaker from './workflows/workflow-maker/main';
|
||||
import workflowControls from './workflows/workflow-controls/main';
|
||||
import workflowService from './workflows/workflow.service';
|
||||
import WorkflowForm from './workflows.form';
|
||||
import CompletedJobsList from './completed-jobs.list';
|
||||
import InventorySourcesList from './inventory-sources.list';
|
||||
import TemplateList from './templates.list';
|
||||
import TemplatesStrings from './templates.strings';
|
||||
import listRoute from '~features/templates/list.route.js';
|
||||
import templateCompletedJobsRoute from '~features/jobs/routes/templateCompletedJobs.route.js';
|
||||
|
||||
export default
|
||||
angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, prompt.name, workflowAdd.name, workflowEdit.name,
|
||||
@ -29,7 +29,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
|
||||
.service('TemplatesService', templatesService)
|
||||
.service('WorkflowService', workflowService)
|
||||
.factory('WorkflowForm', WorkflowForm)
|
||||
.factory('CompletedJobsList', CompletedJobsList)
|
||||
// TODO: currently being kept arround for rbac selection, templates within projects and orgs, etc.
|
||||
.factory('TemplateList', TemplateList)
|
||||
.value('InventorySourcesList', InventorySourcesList)
|
||||
@ -151,10 +150,7 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
|
||||
modes: ['edit'],
|
||||
form: 'JobTemplateForm',
|
||||
controllers: {
|
||||
edit: 'JobTemplateEdit',
|
||||
related: {
|
||||
completed_jobs: 'JobsList'
|
||||
}
|
||||
edit: 'JobTemplateEdit'
|
||||
},
|
||||
data: {
|
||||
activityStream: true,
|
||||
@ -742,6 +738,7 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
|
||||
return result.concat(definition.states);
|
||||
}, [
|
||||
stateExtender.buildDefinition(listRoute),
|
||||
stateExtender.buildDefinition(templateCompletedJobsRoute),
|
||||
stateExtender.buildDefinition(workflowMaker)
|
||||
])
|
||||
};
|
||||
|
||||
1
tower-license
Submodule
1
tower-license
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 009cc73a80e5123143e9c780c7011903d4c2a331
|
||||
Loading…
x
Reference in New Issue
Block a user