mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 09:27:36 -02:30
Timeout, socket and activity stream changes for workflow pause approve
This commit is contained in:
@@ -10,12 +10,7 @@ export default {
|
|||||||
name: 'organizations.job_templates',
|
name: 'organizations.job_templates',
|
||||||
data: {
|
data: {
|
||||||
activityStream: true,
|
activityStream: true,
|
||||||
activityStreamTarget: 'template',
|
activityStreamTarget: 'template'
|
||||||
socket: {
|
|
||||||
"groups": {
|
|
||||||
"jobs": ["status_changed"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
template_search: {
|
template_search: {
|
||||||
|
|||||||
@@ -13,12 +13,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
activityStream: true,
|
activityStream: true,
|
||||||
activityStreamTarget: 'template',
|
activityStreamTarget: 'template'
|
||||||
socket: {
|
|
||||||
"groups": {
|
|
||||||
"jobs": ["status_changed"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
template_search: {
|
template_search: {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
z-index: 1041;
|
z-index: 1041;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
&-drawer {
|
&--drawer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -19,13 +19,13 @@
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-header {
|
&--header {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-title {
|
&--title {
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
color: @default-interface-txt;
|
color: @default-interface-txt;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
width: calc(82%);
|
width: calc(82%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&-actionRow {
|
&--actionRow {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-exit {
|
&--exit {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
@@ -61,4 +61,8 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--expires {
|
||||||
|
color: @default-err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="at-ApprovalsDrawer">
|
<div class="at-ApprovalsDrawer">
|
||||||
<div class="at-ApprovalsDrawer-drawer" ng-if="vm.listLoaded">
|
<div class="at-ApprovalsDrawer--drawer" ng-if="vm.listLoaded">
|
||||||
<div class="at-ApprovalsDrawer-header">
|
<div class="at-ApprovalsDrawer--header">
|
||||||
<div class="at-ApprovalsDrawer-title">
|
<div class="at-ApprovalsDrawer--title">
|
||||||
<span>
|
<span>
|
||||||
{{:: vm.strings.get('approvals.NOTIFICATIONS') }}
|
{{:: vm.strings.get('approvals.NOTIFICATIONS') }}
|
||||||
</span>
|
</span>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
{{vm.count}}
|
{{vm.count}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="at-ApprovalsDrawer-exit">
|
<div class="at-ApprovalsDrawer--exit">
|
||||||
<button class="close" ng-click="closeApprovals()">
|
<button class="close" ng-click="closeApprovals()">
|
||||||
<i class="fa fa-times-circle"></i>
|
<i class="fa fa-times-circle"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -39,19 +39,20 @@
|
|||||||
<at-row-item
|
<at-row-item
|
||||||
value-bind-html="{{ approval.created | longDate }}">
|
value-bind-html="{{ approval.created | longDate }}">
|
||||||
</at-row-item>
|
</at-row-item>
|
||||||
<!-- todo: clean this up if it becomes a real thing-->
|
<!-- todo: translate strings -->
|
||||||
<at-row-item
|
<at-row-item
|
||||||
style="color: red;"
|
ng-if="approval.approval_expiration"
|
||||||
value-bind-html="Expires: Never">
|
class="at-ApprovalsDrawer--expires"
|
||||||
|
value-bind-html="{{:: vm.strings.get('approvals.EXPIRES') }} {{ approval.approval_expiration | longDate }}">
|
||||||
|
</at-row-item>
|
||||||
|
<at-row-item
|
||||||
|
ng-if="!approval.approval_expiration"
|
||||||
|
class="at-ApprovalsDrawer--expires"
|
||||||
|
value-bind-html="{{:: vm.strings.get('approvals.EXPIRES_NEVER') }}">
|
||||||
</at-row-item>
|
</at-row-item>
|
||||||
<!-- todo: hook this up when timeout is active-->
|
|
||||||
<!-- <at-row-item
|
|
||||||
style="color: red;"
|
|
||||||
value-bind-html="Expires {{ approval.created | longDate }}">
|
|
||||||
</at-row-item> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="at-Row-container--wrapped">
|
<div class="at-Row-container--wrapped" ng-if="approval.can_approve_or_deny">
|
||||||
<div class="at-ApprovalsDrawer-actionRow">
|
<div class="at-ApprovalsDrawer--actionRow">
|
||||||
<div>{{:: vm.strings.get('approvals.CONTINUE') }}</div>
|
<div>{{:: vm.strings.get('approvals.CONTINUE') }}</div>
|
||||||
<button class="btn at-Button--success"
|
<button class="btn at-Button--success"
|
||||||
ng-click="vm.approve(approval)"
|
ng-click="vm.approve(approval)"
|
||||||
|
|||||||
@@ -126,7 +126,9 @@ function ComponentsStrings (BaseString) {
|
|||||||
DENY: t.s('DENY'),
|
DENY: t.s('DENY'),
|
||||||
CONTINUE: t.s('Continue workflow job?'),
|
CONTINUE: t.s('Continue workflow job?'),
|
||||||
NOTIFICATIONS: t.s('NOTIFICATIONS'),
|
NOTIFICATIONS: t.s('NOTIFICATIONS'),
|
||||||
WORKFLOW_TEMPLATE: t.s('Workflow Template')
|
WORKFLOW_TEMPLATE: t.s('Workflow Template'),
|
||||||
|
EXPIRES: t.s('Expires:'),
|
||||||
|
EXPIRES_NEVER: t.s('Expires: Never')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,5 +110,5 @@
|
|||||||
<ng-transclude></ng-transclude>
|
<ng-transclude></ng-transclude>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<at-approvals-drawer ng-if="vm.showApprovals" close-approvals="vm.closeApprovals()"></at-approvals-drawer>
|
<at-approvals-drawer ng-if="vm.isLoggedIn && vm.showApprovals" close-approvals="vm.closeApprovals()"></at-approvals-drawer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ export default function BuildAnchor($log, $filter) {
|
|||||||
case 'o_auth2_application':
|
case 'o_auth2_application':
|
||||||
url += `applications/${obj.id}`;
|
url += `applications/${obj.id}`;
|
||||||
break;
|
break;
|
||||||
|
case 'workflow_approval':
|
||||||
|
url += `workflows/${activity.summary_fields.workflow_job[0].id}`
|
||||||
|
name = activity.summary_fields.workflow_job[0].name;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
url += resource + 's/' + obj.id + '/';
|
url += resource + 's/' + obj.id + '/';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,20 @@ export default function BuildDescription(BuildAnchor, $log, i18n) {
|
|||||||
break;
|
break;
|
||||||
// expected outcome: "operation <object1>"
|
// expected outcome: "operation <object1>"
|
||||||
case 'update':
|
case 'update':
|
||||||
activity.description += activity.object1 + BuildAnchor(activity.summary_fields[activity.object1][0], activity.object1, activity);
|
if (activity.object1 === 'workflow_approval'
|
||||||
|
&& _.has(activity, 'changes.status')
|
||||||
|
&& activity.changes.status.length === 2
|
||||||
|
) {
|
||||||
|
let operationText = '';
|
||||||
|
if (activity.changes.status[1] === 'successful') {
|
||||||
|
operationText = i18n._('approved');
|
||||||
|
} else if (activity.changes.status[1] === 'failed') {
|
||||||
|
operationText = i18n._('denied');
|
||||||
|
}
|
||||||
|
activity.description = `${operationText} ${activity.object1} ${BuildAnchor(activity.summary_fields[activity.object1][0], activity.object1, activity)}`;
|
||||||
|
} else {
|
||||||
|
activity.description += activity.object1 + BuildAnchor(activity.summary_fields[activity.object1][0], activity.object1, activity);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'create':
|
case 'create':
|
||||||
activity.description += activity.object1 + BuildAnchor(activity.changes, activity.object1, activity);
|
activity.description += activity.object1 + BuildAnchor(activity.changes, activity.object1, activity);
|
||||||
|
|||||||
@@ -50,11 +50,22 @@ export default ['templateUrl', 'i18n', function(templateUrl, i18n) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let search = {
|
let search = {
|
||||||
or__object1__in: $scope.streamTarget && $scope.streamTarget === 'template' ? 'job_template,workflow_job_template' : $scope.streamTarget,
|
or__object1__in: $scope.streamTarget,
|
||||||
or__object2__in: $scope.streamTarget && $scope.streamTarget === 'template' ? 'job_template,workflow_job_template' : $scope.streamTarget,
|
or__object2__in: $scope.streamTarget,
|
||||||
page_size: '20',
|
page_size: '20',
|
||||||
order_by: '-timestamp'
|
order_by: '-timestamp'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ($scope.streamTarget && $scope.streamTarget === 'template') {
|
||||||
|
search.or__object1__in = 'job_template,workflow_job_template';
|
||||||
|
search.or__object2__in = 'job_template,workflow_job_template';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($scope.streamTarget && $scope.streamTarget === 'job') {
|
||||||
|
search.or__object1__in = 'job,workflow_approval';
|
||||||
|
search.or__object2__in = 'job,workflow_approval';
|
||||||
|
}
|
||||||
|
|
||||||
// Attach the taget to the query parameters
|
// Attach the taget to the query parameters
|
||||||
$state.go('activityStream', {target: $scope.streamTarget, id: null, activity_search: search});
|
$state.go('activityStream', {target: $scope.streamTarget, id: null, activity_search: search});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,10 @@ angular
|
|||||||
document.title = `Ansible ${$rootScope.BRAND_NAME} ${title}`;
|
document.title = `Ansible ${$rootScope.BRAND_NAME} ${title}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$rootScope.$on('ws-approval', () => {
|
||||||
|
fetchApprovalsCount();
|
||||||
|
});
|
||||||
|
|
||||||
function activateTab() {
|
function activateTab() {
|
||||||
// Make the correct tab active
|
// Make the correct tab active
|
||||||
var base = $location.path().replace(/^\//, '').split('/')[0];
|
var base = $location.path().replace(/^\//, '').split('/')[0];
|
||||||
@@ -210,6 +214,20 @@ angular
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchApprovalsCount() {
|
||||||
|
Rest.setUrl(`${GetBasePath('workflow_approvals')}?status=pending&page_size=1`);
|
||||||
|
Rest.get()
|
||||||
|
.then(({data}) => {
|
||||||
|
$rootScope.pendingApprovalCount = data.count;
|
||||||
|
})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors({}, data, status, null, {
|
||||||
|
hdr: i18n._('Error!'),
|
||||||
|
msg: i18n._('Failed to get workflow jobs pending approval. GET returned status: ') + status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if ($rootScope.removeConfigReady) {
|
if ($rootScope.removeConfigReady) {
|
||||||
$rootScope.removeConfigReady();
|
$rootScope.removeConfigReady();
|
||||||
}
|
}
|
||||||
@@ -387,18 +405,7 @@ angular
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
fetchApprovalsCount();
|
||||||
Rest.setUrl(`${GetBasePath('workflow_approvals')}?status=pending&page_size=1`);
|
|
||||||
Rest.get()
|
|
||||||
.then(({data}) => {
|
|
||||||
$rootScope.pendingApprovalCount = data.count;
|
|
||||||
})
|
|
||||||
.catch(({data, status}) => {
|
|
||||||
ProcessErrors({}, data, status, null, {
|
|
||||||
hdr: i18n._('Error!'),
|
|
||||||
msg: i18n._('Failed to get workflow jobs pending approval. GET returned status: ') + status
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,11 +75,22 @@ export default
|
|||||||
stateGoParams.target = streamConfig.activityStreamTarget;
|
stateGoParams.target = streamConfig.activityStreamTarget;
|
||||||
let isTemplateTarget = _.includes(['template', 'job_template', 'workflow_job_template'], streamConfig.activityStreamTarget);
|
let isTemplateTarget = _.includes(['template', 'job_template', 'workflow_job_template'], streamConfig.activityStreamTarget);
|
||||||
stateGoParams.activity_search = {
|
stateGoParams.activity_search = {
|
||||||
or__object1__in: isTemplateTarget ? 'job_template,workflow_job_template' : streamConfig.activityStreamTarget,
|
or__object1__in: streamConfig.activityStreamTarget,
|
||||||
or__object2__in: isTemplateTarget ? 'job_template,workflow_job_template' : streamConfig.activityStreamTarget,
|
or__object2__in: streamConfig.activityStreamTarget,
|
||||||
order_by: '-timestamp',
|
order_by: '-timestamp',
|
||||||
page_size: '20',
|
page_size: '20',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isTemplateTarget) {
|
||||||
|
stateGoParams.activity_search.or__object1__in = 'job_template,workflow_job_template';
|
||||||
|
stateGoParams.activity_search.or__object2__in = 'job_template,workflow_job_template';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamConfig.activityStreamTarget === 'job') {
|
||||||
|
stateGoParams.activity_search.or__object1__in = 'job,workflow_approval';
|
||||||
|
stateGoParams.activity_search.or__object2__in = 'job,workflow_approval';
|
||||||
|
}
|
||||||
|
|
||||||
if (streamConfig.activityStreamTarget && streamConfig.activityStreamId && !streamConfig.noActivityStreamID) {
|
if (streamConfig.activityStreamTarget && streamConfig.activityStreamId && !streamConfig.noActivityStreamID) {
|
||||||
stateGoParams.activity_search[streamConfig.activityStreamTarget] = $state.params[streamConfig.activityStreamId];
|
stateGoParams.activity_search[streamConfig.activityStreamTarget] = $state.params[streamConfig.activityStreamId];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,9 +94,12 @@
|
|||||||
<div class="DashboardGraphs-graphContainer" auto-size-module
|
<div class="DashboardGraphs-graphContainer" auto-size-module
|
||||||
graph-type="jobsStatus"
|
graph-type="jobsStatus"
|
||||||
ng-class="{'is-selected': jobStatusSelected }">
|
ng-class="{'is-selected': jobStatusSelected }">
|
||||||
<job-status-graph class="DashboardGraphs-graph
|
<job-status-graph
|
||||||
DashboardGraphs-graph--jobStatusGraph"
|
class="DashboardGraphs-graph DashboardGraphs-graph--jobStatusGraph"
|
||||||
data="graphData.jobStatus" period="month" job-type="all">
|
data="graphData.jobStatus"
|
||||||
|
period="graphData.period"
|
||||||
|
job-type="graphData.jobType"
|
||||||
|
status="graphData.status">
|
||||||
</job-status-graph>
|
</job-status-graph>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,19 +18,17 @@ function JobStatusGraph($window, adjustGraphSize, templateUrl, i18n, moment, gra
|
|||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
data: '='
|
data: '=',
|
||||||
|
period: '=',
|
||||||
|
jobType: '=',
|
||||||
|
status: '='
|
||||||
},
|
},
|
||||||
templateUrl: templateUrl('home/dashboard/graphs/job-status/job_status_graph'),
|
templateUrl: templateUrl('home/dashboard/graphs/job-status/job_status_graph'),
|
||||||
link: link
|
link: link
|
||||||
};
|
};
|
||||||
|
|
||||||
function link(scope, element) {
|
function link(scope, element) {
|
||||||
var job_type,
|
var job_status_chart = nv.models.lineChart();
|
||||||
job_status_chart = nv.models.lineChart();
|
|
||||||
|
|
||||||
scope.period="month";
|
|
||||||
scope.jobType="all";
|
|
||||||
scope.status="both";
|
|
||||||
|
|
||||||
scope.$watchCollection('data', function(value) {
|
scope.$watchCollection('data', function(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
@@ -129,8 +127,6 @@ function JobStatusGraph($window, adjustGraphSize, templateUrl, i18n, moment, gra
|
|||||||
|
|
||||||
// when the Period drop down filter is used, create a new graph based on the
|
// when the Period drop down filter is used, create a new graph based on the
|
||||||
$('.n').off('click').on("click", function(){
|
$('.n').off('click').on("click", function(){
|
||||||
period = this.getAttribute("id");
|
|
||||||
|
|
||||||
$('#period-dropdown-display')
|
$('#period-dropdown-display')
|
||||||
.html(`
|
.html(`
|
||||||
<span>${this.text}</span>
|
<span>${this.text}</span>
|
||||||
@@ -139,13 +135,11 @@ function JobStatusGraph($window, adjustGraphSize, templateUrl, i18n, moment, gra
|
|||||||
|
|
||||||
scope.$parent.isFailed = true;
|
scope.$parent.isFailed = true;
|
||||||
scope.$parent.isSuccessful = true;
|
scope.$parent.isSuccessful = true;
|
||||||
recreateGraph(period, job_type);
|
recreateGraph(this.getAttribute("id"), scope.jobType, scope.status);
|
||||||
});
|
});
|
||||||
|
|
||||||
//On click, update with new data
|
//On click, update with new data
|
||||||
$('.m').off('click').on("click", function(){
|
$('.m').off('click').on("click", function(){
|
||||||
job_type = this.getAttribute("id");
|
|
||||||
|
|
||||||
$('#type-dropdown-display')
|
$('#type-dropdown-display')
|
||||||
.html(`
|
.html(`
|
||||||
<span>${this.text}</span>
|
<span>${this.text}</span>
|
||||||
@@ -154,19 +148,17 @@ function JobStatusGraph($window, adjustGraphSize, templateUrl, i18n, moment, gra
|
|||||||
|
|
||||||
scope.$parent.isFailed = true;
|
scope.$parent.isFailed = true;
|
||||||
scope.$parent.isSuccessful = true;
|
scope.$parent.isSuccessful = true;
|
||||||
recreateGraph(period, job_type);
|
recreateGraph(scope.period, this.getAttribute("id"), scope.status);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.o').off('click').on('click', function() {
|
$('.o').off('click').on('click', function() {
|
||||||
var job_status = this.getAttribute('id');
|
|
||||||
|
|
||||||
$('#status-dropdown-display')
|
$('#status-dropdown-display')
|
||||||
.html(`
|
.html(`
|
||||||
<span>${this.text}</span>
|
<span>${this.text}</span>
|
||||||
<i class="fa fa-angle-down DashboardGraphs-filterIcon"></i>
|
<i class="fa fa-angle-down DashboardGraphs-filterIcon"></i>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
recreateGraph(scope.period, scope.jobType, job_status);
|
recreateGraph(scope.period, scope.jobType, this.getAttribute("id"));
|
||||||
});
|
});
|
||||||
|
|
||||||
adjustGraphSize(job_status_chart, element);
|
adjustGraphSize(job_status_chart, element);
|
||||||
|
|||||||
@@ -4,95 +4,44 @@
|
|||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
export default
|
export default ["Rest", "GetBasePath", "ProcessErrors", "$q", JobStatusGraphData];
|
||||||
["Rest",
|
|
||||||
"GetBasePath",
|
|
||||||
"ProcessErrors",
|
|
||||||
"$rootScope",
|
|
||||||
"$q",
|
|
||||||
"$timeout",
|
|
||||||
JobStatusGraphData];
|
|
||||||
|
|
||||||
function JobStatusGraphData(Rest, getBasePath, processErrors, $rootScope, $q, $timeout) {
|
|
||||||
|
|
||||||
function getData(period, jobType, status) {
|
|
||||||
var url, dash_path = getBasePath('dashboard');
|
|
||||||
if(dash_path === '' ){
|
|
||||||
processErrors(null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null, {
|
|
||||||
hdr: 'Error!',
|
|
||||||
msg: "There was an error. Please try again."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
url = dash_path + 'graphs/jobs/?period='+period+'&job_type='+jobType;
|
|
||||||
Rest.setHeader({'X-WS-Session-Quiet': true});
|
|
||||||
Rest.setUrl(url);
|
|
||||||
return Rest.get()
|
|
||||||
.then(function(value) {
|
|
||||||
if(status === "successful" || status === "failed"){
|
|
||||||
delete value.data.jobs[status];
|
|
||||||
}
|
|
||||||
return value.data;
|
|
||||||
})
|
|
||||||
.catch(function(response) {
|
|
||||||
var errorMessage = 'Failed to get: ' + response.url + ' GET returned: ' + response.status;
|
|
||||||
|
|
||||||
processErrors(null,
|
|
||||||
response.data,
|
|
||||||
response.status,
|
|
||||||
null, {
|
|
||||||
hdr: 'Error!',
|
|
||||||
msg: errorMessage
|
|
||||||
});
|
|
||||||
return $q.reject(response);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function JobStatusGraphData(Rest, getBasePath, processErrors, $q) {
|
||||||
return {
|
return {
|
||||||
pendingRefresh: false,
|
|
||||||
refreshTimerRunning: false,
|
|
||||||
refreshTimer: angular.noop,
|
|
||||||
destroyWatcher: angular.noop,
|
|
||||||
setupWatcher: function(period, jobType) {
|
|
||||||
const that = this;
|
|
||||||
that.destroyWatcher =
|
|
||||||
$rootScope.$on('ws-jobs', function() {
|
|
||||||
if (!that.refreshTimerRunning) {
|
|
||||||
that.timebandGetData(period, jobType);
|
|
||||||
} else {
|
|
||||||
that.pendingRefresh = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
timebandGetData: function(period, jobType) {
|
|
||||||
getData(period, jobType).then(function(result) {
|
|
||||||
$rootScope.
|
|
||||||
$broadcast('DataReceived:JobStatusGraph',
|
|
||||||
result);
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
this.pendingRefresh = false;
|
|
||||||
this.refreshTimerRunning = true;
|
|
||||||
this.refreshTimer = $timeout(() => {
|
|
||||||
if (this.pendingRefresh) {
|
|
||||||
this.timebandGetData(period, jobType);
|
|
||||||
} else {
|
|
||||||
this.refreshTimerRunning = false;
|
|
||||||
}
|
|
||||||
}, 5000);
|
|
||||||
},
|
|
||||||
get: function(period, jobType, status) {
|
get: function(period, jobType, status) {
|
||||||
|
var url, dash_path = getBasePath('dashboard');
|
||||||
this.destroyWatcher();
|
if(dash_path === '' ){
|
||||||
$timeout.cancel(this.refreshTimer);
|
processErrors(null,
|
||||||
this.refreshTimerRunning = false;
|
null,
|
||||||
this.pendingRefresh = false;
|
null,
|
||||||
this.setupWatcher(period, jobType);
|
null, {
|
||||||
|
hdr: 'Error!',
|
||||||
return getData(period, jobType, status);
|
msg: "There was an error. Please try again."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
url = dash_path + 'graphs/jobs/?period='+period+'&job_type='+jobType;
|
||||||
|
Rest.setHeader({'X-WS-Session-Quiet': true});
|
||||||
|
Rest.setUrl(url);
|
||||||
|
return Rest.get()
|
||||||
|
.then(function(value) {
|
||||||
|
if(status === "successful" || status === "failed"){
|
||||||
|
delete value.data.jobs[status];
|
||||||
|
}
|
||||||
|
return value.data;
|
||||||
|
})
|
||||||
|
.catch(function(response) {
|
||||||
|
var errorMessage = 'Failed to get: ' + response.url + ' GET returned: ' + response.status;
|
||||||
|
|
||||||
|
processErrors(null,
|
||||||
|
response.data,
|
||||||
|
response.status,
|
||||||
|
null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: errorMessage
|
||||||
|
});
|
||||||
|
return $q.reject(response);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
export default ['$scope', '$rootScope','Wait', '$timeout',
|
export default ['$scope','Wait', '$timeout', 'i18n',
|
||||||
'Rest', 'GetBasePath', 'ProcessErrors', 'graphData',
|
'Rest', 'GetBasePath', 'ProcessErrors', 'graphData',
|
||||||
function($scope, $rootScope, Wait, $timeout,
|
function($scope, Wait, $timeout, i18n,
|
||||||
Rest, GetBasePath, ProcessErrors, graphData) {
|
Rest, GetBasePath, ProcessErrors, graphData) {
|
||||||
|
|
||||||
var dataCount = 0;
|
var dataCount = 0;
|
||||||
@@ -53,16 +53,10 @@ export default ['$scope', '$rootScope','Wait', '$timeout',
|
|||||||
$scope.removeDashboardReady = $scope.$on('dashboardReady', function (e, data) {
|
$scope.removeDashboardReady = $scope.$on('dashboardReady', function (e, data) {
|
||||||
$scope.dashboardCountsData = data;
|
$scope.dashboardCountsData = data;
|
||||||
$scope.graphData = graphData;
|
$scope.graphData = graphData;
|
||||||
|
$scope.graphData.period = "month";
|
||||||
|
$scope.graphData.jobType = "all";
|
||||||
|
$scope.graphData.status = "both";
|
||||||
$scope.$emit('dashboardDataLoadComplete');
|
$scope.$emit('dashboardDataLoadComplete');
|
||||||
|
|
||||||
var cleanupJobListener =
|
|
||||||
$rootScope.$on('DataReceived:JobStatusGraph', function(e, data) {
|
|
||||||
$scope.graphData.jobStatus = data;
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.$on('$destroy', function() {
|
|
||||||
cleanupJobListener();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($scope.removeDashboardJobsListReady) {
|
if ($scope.removeDashboardJobsListReady) {
|
||||||
@@ -81,40 +75,6 @@ export default ['$scope', '$rootScope','Wait', '$timeout',
|
|||||||
$scope.$emit('dashboardDataLoadComplete');
|
$scope.$emit('dashboardDataLoadComplete');
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.refresh = function () {
|
|
||||||
Wait('start');
|
|
||||||
Rest.setUrl(GetBasePath('dashboard'));
|
|
||||||
Rest.get()
|
|
||||||
.then(({data}) => {
|
|
||||||
$scope.dashboardData = data;
|
|
||||||
$scope.$emit('dashboardReady', data);
|
|
||||||
})
|
|
||||||
.catch(({data, status}) => {
|
|
||||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard: ' + status });
|
|
||||||
});
|
|
||||||
Rest.setUrl(GetBasePath("unified_jobs") + "?order_by=-finished&page_size=5&finished__isnull=false&type=workflow_job,job");
|
|
||||||
Rest.setHeader({'X-WS-Session-Quiet': true});
|
|
||||||
Rest.get()
|
|
||||||
.then(({data}) => {
|
|
||||||
data = data.results;
|
|
||||||
$scope.$emit('dashboardJobsListReady', data);
|
|
||||||
})
|
|
||||||
.catch(({data, status}) => {
|
|
||||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status });
|
|
||||||
});
|
|
||||||
Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template");
|
|
||||||
Rest.get()
|
|
||||||
.then(({data}) => {
|
|
||||||
data = data.results;
|
|
||||||
$scope.$emit('dashboardJobTemplatesListReady', data);
|
|
||||||
})
|
|
||||||
.catch(({data, status}) => {
|
|
||||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard job templates list: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.refresh();
|
|
||||||
|
|
||||||
function refreshLists () {
|
function refreshLists () {
|
||||||
Rest.setUrl(GetBasePath('dashboard'));
|
Rest.setUrl(GetBasePath('dashboard'));
|
||||||
Rest.get()
|
Rest.get()
|
||||||
@@ -122,7 +82,7 @@ export default ['$scope', '$rootScope','Wait', '$timeout',
|
|||||||
$scope.dashboardData = data;
|
$scope.dashboardData = data;
|
||||||
})
|
})
|
||||||
.catch(({data, status}) => {
|
.catch(({data, status}) => {
|
||||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard host graph data: ' + status });
|
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n._(`Failed to get dashboard host graph data: ${status}`) });
|
||||||
});
|
});
|
||||||
|
|
||||||
Rest.setUrl(GetBasePath("unified_jobs") + "?order_by=-finished&page_size=5&finished__isnull=false&type=workflow_job,job");
|
Rest.setUrl(GetBasePath("unified_jobs") + "?order_by=-finished&page_size=5&finished__isnull=false&type=workflow_job,job");
|
||||||
@@ -132,7 +92,7 @@ export default ['$scope', '$rootScope','Wait', '$timeout',
|
|||||||
$scope.dashboardJobsListData = data.results;
|
$scope.dashboardJobsListData = data.results;
|
||||||
})
|
})
|
||||||
.catch(({data, status}) => {
|
.catch(({data, status}) => {
|
||||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status });
|
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n._(`Failed to get dashboard jobs list: ${status}`) });
|
||||||
});
|
});
|
||||||
|
|
||||||
Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template");
|
Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template");
|
||||||
@@ -141,8 +101,24 @@ export default ['$scope', '$rootScope','Wait', '$timeout',
|
|||||||
$scope.dashboardJobTemplatesListData = data.results;
|
$scope.dashboardJobTemplatesListData = data.results;
|
||||||
})
|
})
|
||||||
.catch(({data, status}) => {
|
.catch(({data, status}) => {
|
||||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status });
|
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n._(`Failed to get dashboard jobs list: ${status}`) });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ($scope.graphData) {
|
||||||
|
Rest.setUrl(`${GetBasePath('dashboard')}graphs/jobs/?period=${$scope.graphData.period}&job_type=${$scope.graphData.jobType}`);
|
||||||
|
Rest.setHeader({'X-WS-Session-Quiet': true});
|
||||||
|
Rest.get()
|
||||||
|
.then(function(value) {
|
||||||
|
if($scope.graphData.status === "successful" || $scope.graphData.status === "failed"){
|
||||||
|
delete value.data.jobs[$scope.graphData.status];
|
||||||
|
}
|
||||||
|
$scope.graphData.jobStatus = value.data;
|
||||||
|
})
|
||||||
|
.catch(function({data, status}) {
|
||||||
|
processErrors(null, data, status, null, { hdr: i18n._('Error!'), msg: i18n._(`Failed to get dashboard graph data: ${response.status}`)});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pendingRefresh = false;
|
pendingRefresh = false;
|
||||||
refreshTimerRunning = true;
|
refreshTimerRunning = true;
|
||||||
$timeout(() => {
|
$timeout(() => {
|
||||||
@@ -154,5 +130,35 @@ export default ['$scope', '$rootScope','Wait', '$timeout',
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(GetBasePath('dashboard'));
|
||||||
|
Rest.get()
|
||||||
|
.then(({data}) => {
|
||||||
|
$scope.dashboardData = data;
|
||||||
|
$scope.$emit('dashboardReady', data);
|
||||||
|
})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n._(`Failed to get dashboard: ${status}`) });
|
||||||
|
});
|
||||||
|
Rest.setUrl(GetBasePath("unified_jobs") + "?order_by=-finished&page_size=5&finished__isnull=false&type=workflow_job,job");
|
||||||
|
Rest.setHeader({'X-WS-Session-Quiet': true});
|
||||||
|
Rest.get()
|
||||||
|
.then(({data}) => {
|
||||||
|
data = data.results;
|
||||||
|
$scope.$emit('dashboardJobsListReady', data);
|
||||||
|
})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n._(`Failed to get dashboard jobs list: ${status}`) });
|
||||||
|
});
|
||||||
|
Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template");
|
||||||
|
Rest.get()
|
||||||
|
.then(({data}) => {
|
||||||
|
data = data.results;
|
||||||
|
$scope.$emit('dashboardJobTemplatesListReady', data);
|
||||||
|
})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n._(`Failed to get dashboard job templates list: ${status}`) });
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -10,12 +10,7 @@ export default {
|
|||||||
params: { licenseMissing: null },
|
params: { licenseMissing: null },
|
||||||
data: {
|
data: {
|
||||||
activityStream: true,
|
activityStream: true,
|
||||||
refreshButton: true,
|
refreshButton: true
|
||||||
socket: {
|
|
||||||
"groups": {
|
|
||||||
"jobs": ["status_changed"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
ncyBreadcrumb: {
|
ncyBreadcrumb: {
|
||||||
label: N_("DASHBOARD")
|
label: N_("DASHBOARD")
|
||||||
|
|||||||
@@ -175,12 +175,7 @@ let lists = [{
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
activityStream: true,
|
activityStream: true,
|
||||||
activityStreamTarget: 'organization',
|
activityStreamTarget: 'organization'
|
||||||
socket: {
|
|
||||||
"groups": {
|
|
||||||
"jobs": ["status_changed"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
ncyBreadcrumb: {
|
ncyBreadcrumb: {
|
||||||
parent: "organizations.edit",
|
parent: "organizations.edit",
|
||||||
|
|||||||
@@ -96,6 +96,14 @@ export default
|
|||||||
$log.debug('Received From Server: ' + e.data);
|
$log.debug('Received From Server: ' + e.data);
|
||||||
|
|
||||||
var data = JSON.parse(e.data), str = "";
|
var data = JSON.parse(e.data), str = "";
|
||||||
|
|
||||||
|
if (data.group_name === 'jobs'
|
||||||
|
&& 'type' in data
|
||||||
|
&& data.type === 'workflow_approval'
|
||||||
|
) {
|
||||||
|
$rootScope.$broadcast('ws-approval');
|
||||||
|
}
|
||||||
|
|
||||||
if(!window.liveUpdates && data.group_name !== "control" && $state.current.name !== "output"){
|
if(!window.liveUpdates && data.group_name !== "control" && $state.current.name !== "output"){
|
||||||
$log.debug('Message from server dropped: ' + e.data);
|
$log.debug('Message from server dropped: ' + e.data);
|
||||||
needsRefreshAfterBlur = true;
|
needsRefreshAfterBlur = true;
|
||||||
@@ -254,21 +262,23 @@ export default
|
|||||||
// requires a subscribe or an unsubscribe
|
// requires a subscribe or an unsubscribe
|
||||||
var self = this;
|
var self = this;
|
||||||
return socketPromise.promise.then(function(){
|
return socketPromise.promise.then(function(){
|
||||||
if(!state.data || !state.data.socket){
|
if (_.get(state, 'data.socket.groups.jobs')) {
|
||||||
_.merge(state.data, {socket: {groups: {}}});
|
if (!state.data.socket.groups.jobs.includes("status_changed")) {
|
||||||
self.unsubscribe(state);
|
state.data.socket.groups.jobs.push("status_changed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else{
|
else if(!state.data || !state.data.socket){
|
||||||
["job_events", "ad_hoc_command_events", "workflow_events",
|
_.merge(state.data, {socket: {groups: {jobs: ["status_changed"]}}});
|
||||||
|
}
|
||||||
|
["job_events", "ad_hoc_command_events", "workflow_events",
|
||||||
"project_update_events", "inventory_update_events",
|
"project_update_events", "inventory_update_events",
|
||||||
"system_job_events"
|
"system_job_events"
|
||||||
].forEach(function(group) {
|
].forEach(function(group) {
|
||||||
if(state.data && state.data.socket && state.data.socket.groups.hasOwnProperty(group)){
|
if(state.data && state.data.socket && state.data.socket.groups.hasOwnProperty(group)){
|
||||||
state.data.socket.groups[group] = [id];
|
state.data.socket.groups[group] = [id];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.subscribe(state);
|
self.subscribe(state);
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -608,10 +608,23 @@ export default ['$scope', 'TemplatesService',
|
|||||||
}
|
}
|
||||||
} else if ($scope.nodeConfig.mode === "edit") {
|
} else if ($scope.nodeConfig.mode === "edit") {
|
||||||
if (selectedTemplate) {
|
if (selectedTemplate) {
|
||||||
nodeRef[$scope.nodeConfig.nodeId].fullUnifiedJobTemplateObject = selectedTemplate;
|
if (isPauseNode) {
|
||||||
nodeRef[$scope.nodeConfig.nodeId].unifiedJobTemplate = selectedTemplate;
|
// If it's a _new_ pause node then we'll want to create the new ujt
|
||||||
nodeRef[$scope.nodeConfig.nodeId].promptData = _.cloneDeep(promptData);
|
// If it's an existing pause node then we'll want to update the ujt
|
||||||
nodeRef[$scope.nodeConfig.nodeId].isEdited = true;
|
nodeRef[$scope.nodeConfig.nodeId].unifiedJobTemplate = {
|
||||||
|
name: selectedTemplate.name,
|
||||||
|
description: selectedTemplate.description,
|
||||||
|
timeout: selectedTemplate.timeout,
|
||||||
|
unified_job_type: "workflow_approval"
|
||||||
|
};
|
||||||
|
nodeRef[$scope.nodeConfig.nodeId].isEdited = true;
|
||||||
|
} else {
|
||||||
|
nodeRef[$scope.nodeConfig.nodeId].fullUnifiedJobTemplateObject = selectedTemplate;
|
||||||
|
nodeRef[$scope.nodeConfig.nodeId].unifiedJobTemplate = selectedTemplate;
|
||||||
|
nodeRef[$scope.nodeConfig.nodeId].promptData = _.cloneDeep(promptData);
|
||||||
|
nodeRef[$scope.nodeConfig.nodeId].isEdited = true;
|
||||||
|
}
|
||||||
|
|
||||||
$scope.graphState.nodeBeingEdited = null;
|
$scope.graphState.nodeBeingEdited = null;
|
||||||
|
|
||||||
$scope.graphState.arrayOfLinksForChart.map( (link) => {
|
$scope.graphState.arrayOfLinksForChart.map( (link) => {
|
||||||
@@ -622,16 +635,6 @@ export default ['$scope', 'TemplatesService',
|
|||||||
link.source.unifiedJobTemplate = selectedTemplate;
|
link.source.unifiedJobTemplate = selectedTemplate;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (isPauseNode) {
|
|
||||||
// If it's a _new_ pause node then we'll want to create the new ujt
|
|
||||||
// If it's an existing pause node then we'll want to update the ujt
|
|
||||||
nodeRef[$scope.nodeConfig.nodeId].unifiedJobTemplate = {
|
|
||||||
name: selectedTemplate.name,
|
|
||||||
description: selectedTemplate.description,
|
|
||||||
timeout: selectedTemplate.timeout,
|
|
||||||
unified_job_type: "workflow_approval"
|
|
||||||
};
|
|
||||||
nodeRef[$scope.nodeConfig.nodeId].isEdited = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ export default {
|
|||||||
parent: 'jobs',
|
parent: 'jobs',
|
||||||
label: '{{ workflow.id }} - {{ workflow.name }}'
|
label: '{{ workflow.id }} - {{ workflow.name }}'
|
||||||
},
|
},
|
||||||
data: {
|
|
||||||
socket: {
|
|
||||||
"groups":{
|
|
||||||
"jobs": ["status_changed"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
templateUrl: templateUrl('workflow-results/workflow-results'),
|
templateUrl: templateUrl('workflow-results/workflow-results'),
|
||||||
controller: workflowResultsController,
|
controller: workflowResultsController,
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
Reference in New Issue
Block a user