Timeout, socket and activity stream changes for workflow pause approve

This commit is contained in:
mabashian
2019-08-13 12:49:06 -04:00
committed by Ryan Petrello
parent 9186cb23a6
commit adf621d2cf
20 changed files with 237 additions and 248 deletions

View File

@@ -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: {

View File

@@ -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: {

View File

@@ -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;
}
} }

View File

@@ -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)"

View File

@@ -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')
}; };
} }

View File

@@ -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>

View File

@@ -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 + '/';
} }

View File

@@ -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);

View File

@@ -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});
} }

View File

@@ -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
});
});
} }
} }

View File

@@ -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];
} }

View File

@@ -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>

View File

@@ -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);

View File

@@ -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);
});
} }
}; };

View File

@@ -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}`) });
});
} }
]; ];

View File

@@ -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")

View File

@@ -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",

View File

@@ -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;
}); });
} }

View File

@@ -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;
} }
} }

View File

@@ -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: {