diff --git a/awx/ui/client/features/templates/routes/organizationsTemplatesList.route.js b/awx/ui/client/features/templates/routes/organizationsTemplatesList.route.js index c0c7c3fdb7..4d306b2857 100644 --- a/awx/ui/client/features/templates/routes/organizationsTemplatesList.route.js +++ b/awx/ui/client/features/templates/routes/organizationsTemplatesList.route.js @@ -10,12 +10,7 @@ export default { name: 'organizations.job_templates', data: { activityStream: true, - activityStreamTarget: 'template', - socket: { - "groups": { - "jobs": ["status_changed"] - } - } + activityStreamTarget: 'template' }, params: { template_search: { diff --git a/awx/ui/client/features/templates/routes/templatesList.route.js b/awx/ui/client/features/templates/routes/templatesList.route.js index 63d58fb83d..4b17ab471d 100644 --- a/awx/ui/client/features/templates/routes/templatesList.route.js +++ b/awx/ui/client/features/templates/routes/templatesList.route.js @@ -13,12 +13,7 @@ export default { }, data: { activityStream: true, - activityStreamTarget: 'template', - socket: { - "groups": { - "jobs": ["status_changed"] - } - } + activityStreamTarget: 'template' }, params: { template_search: { diff --git a/awx/ui/client/lib/components/approvalsDrawer/_index.less b/awx/ui/client/lib/components/approvalsDrawer/_index.less index 5c637f3591..79771c1d0c 100644 --- a/awx/ui/client/lib/components/approvalsDrawer/_index.less +++ b/awx/ui/client/lib/components/approvalsDrawer/_index.less @@ -8,7 +8,7 @@ z-index: 1041; background-color: rgba(0, 0, 0, 0.3); - &-drawer { + &--drawer { position: absolute; right: 0; top: 0; @@ -19,13 +19,13 @@ overflow-y: scroll; } - &-header { + &--header { display: flex; width: 100%; margin-bottom: 20px; } - &-title { + &--title { flex: 1 0 auto; color: @default-interface-txt; font-size: 14px; @@ -33,7 +33,7 @@ width: calc(82%); } - &-actionRow { + &--actionRow { display: flex; justify-content: flex-end; width: 100%; @@ -44,7 +44,7 @@ } } - &-exit { + &--exit { justify-content: flex-end; display: flex; @@ -61,4 +61,8 @@ opacity: 1; } } + + &--expires { + color: @default-err; + } } \ No newline at end of file diff --git a/awx/ui/client/lib/components/approvalsDrawer/approvalsDrawer.partial.html b/awx/ui/client/lib/components/approvalsDrawer/approvalsDrawer.partial.html index 1b476efcf2..01ff402042 100644 --- a/awx/ui/client/lib/components/approvalsDrawer/approvalsDrawer.partial.html +++ b/awx/ui/client/lib/components/approvalsDrawer/approvalsDrawer.partial.html @@ -1,7 +1,7 @@
-
-
-
+
+
+
{{:: vm.strings.get('approvals.NOTIFICATIONS') }} @@ -9,7 +9,7 @@ {{vm.count}}
-
+
@@ -39,19 +39,20 @@ - + + ng-if="approval.approval_expiration" + class="at-ApprovalsDrawer--expires" + value-bind-html="{{:: vm.strings.get('approvals.EXPIRES') }} {{ approval.approval_expiration | longDate }}"> + + - -
-
-
+
+
{{:: vm.strings.get('approvals.CONTINUE') }}
- +
diff --git a/awx/ui/client/src/activity-stream/factories/build-anchor.factory.js b/awx/ui/client/src/activity-stream/factories/build-anchor.factory.js index 5a9435c10a..58d262b26a 100644 --- a/awx/ui/client/src/activity-stream/factories/build-anchor.factory.js +++ b/awx/ui/client/src/activity-stream/factories/build-anchor.factory.js @@ -93,6 +93,10 @@ export default function BuildAnchor($log, $filter) { case 'o_auth2_application': url += `applications/${obj.id}`; break; + case 'workflow_approval': + url += `workflows/${activity.summary_fields.workflow_job[0].id}` + name = activity.summary_fields.workflow_job[0].name; + break; default: url += resource + 's/' + obj.id + '/'; } diff --git a/awx/ui/client/src/activity-stream/factories/build-description.factory.js b/awx/ui/client/src/activity-stream/factories/build-description.factory.js index ddb05b0662..ea7c3aaef1 100644 --- a/awx/ui/client/src/activity-stream/factories/build-description.factory.js +++ b/awx/ui/client/src/activity-stream/factories/build-description.factory.js @@ -124,7 +124,20 @@ export default function BuildDescription(BuildAnchor, $log, i18n) { break; // expected outcome: "operation " 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; case 'create': activity.description += activity.object1 + BuildAnchor(activity.changes, activity.object1, activity); diff --git a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js index e6b0ff87be..3dbf3a0d14 100644 --- a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js +++ b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js @@ -50,11 +50,22 @@ export default ['templateUrl', 'i18n', function(templateUrl, i18n) { } else { let search = { - or__object1__in: $scope.streamTarget && $scope.streamTarget === 'template' ? 'job_template,workflow_job_template' : $scope.streamTarget, - or__object2__in: $scope.streamTarget && $scope.streamTarget === 'template' ? 'job_template,workflow_job_template' : $scope.streamTarget, + or__object1__in: $scope.streamTarget, + or__object2__in: $scope.streamTarget, page_size: '20', 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 $state.go('activityStream', {target: $scope.streamTarget, id: null, activity_search: search}); } diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 5dac887aba..54e05c4a1e 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -198,6 +198,10 @@ angular document.title = `Ansible ${$rootScope.BRAND_NAME} ${title}`; }); + $rootScope.$on('ws-approval', () => { + fetchApprovalsCount(); + }); + function activateTab() { // Make the correct tab active 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) { $rootScope.removeConfigReady(); } @@ -387,18 +405,7 @@ angular } }); }); - - 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 - }); - }); + fetchApprovalsCount(); } } diff --git a/awx/ui/client/src/bread-crumb/bread-crumb.directive.js b/awx/ui/client/src/bread-crumb/bread-crumb.directive.js index c3f8466b81..47c1b82e61 100644 --- a/awx/ui/client/src/bread-crumb/bread-crumb.directive.js +++ b/awx/ui/client/src/bread-crumb/bread-crumb.directive.js @@ -75,11 +75,22 @@ export default stateGoParams.target = streamConfig.activityStreamTarget; let isTemplateTarget = _.includes(['template', 'job_template', 'workflow_job_template'], streamConfig.activityStreamTarget); stateGoParams.activity_search = { - or__object1__in: isTemplateTarget ? 'job_template,workflow_job_template' : streamConfig.activityStreamTarget, - or__object2__in: isTemplateTarget ? 'job_template,workflow_job_template' : streamConfig.activityStreamTarget, + or__object1__in: streamConfig.activityStreamTarget, + or__object2__in: streamConfig.activityStreamTarget, order_by: '-timestamp', 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) { stateGoParams.activity_search[streamConfig.activityStreamTarget] = $state.params[streamConfig.activityStreamId]; } diff --git a/awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.partial.html b/awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.partial.html index d9fac74d47..4d1d547a86 100644 --- a/awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.partial.html +++ b/awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.partial.html @@ -94,9 +94,12 @@
- +
diff --git a/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js b/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js index 96e0b81786..51b56ec3bd 100644 --- a/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js +++ b/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js @@ -18,19 +18,17 @@ function JobStatusGraph($window, adjustGraphSize, templateUrl, i18n, moment, gra return { restrict: 'E', scope: { - data: '=' + data: '=', + period: '=', + jobType: '=', + status: '=' }, templateUrl: templateUrl('home/dashboard/graphs/job-status/job_status_graph'), link: link }; function link(scope, element) { - var job_type, - job_status_chart = nv.models.lineChart(); - - scope.period="month"; - scope.jobType="all"; - scope.status="both"; + var job_status_chart = nv.models.lineChart(); scope.$watchCollection('data', function(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 $('.n').off('click').on("click", function(){ - period = this.getAttribute("id"); - $('#period-dropdown-display') .html(` ${this.text} @@ -139,13 +135,11 @@ function JobStatusGraph($window, adjustGraphSize, templateUrl, i18n, moment, gra scope.$parent.isFailed = true; scope.$parent.isSuccessful = true; - recreateGraph(period, job_type); + recreateGraph(this.getAttribute("id"), scope.jobType, scope.status); }); //On click, update with new data $('.m').off('click').on("click", function(){ - job_type = this.getAttribute("id"); - $('#type-dropdown-display') .html(` ${this.text} @@ -154,19 +148,17 @@ function JobStatusGraph($window, adjustGraphSize, templateUrl, i18n, moment, gra scope.$parent.isFailed = true; scope.$parent.isSuccessful = true; - recreateGraph(period, job_type); + recreateGraph(scope.period, this.getAttribute("id"), scope.status); }); $('.o').off('click').on('click', function() { - var job_status = this.getAttribute('id'); - $('#status-dropdown-display') .html(` ${this.text} `); - recreateGraph(scope.period, scope.jobType, job_status); + recreateGraph(scope.period, scope.jobType, this.getAttribute("id")); }); adjustGraphSize(job_status_chart, element); diff --git a/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.service.js b/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.service.js index 100fc24bd1..8d7f5fb690 100644 --- a/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.service.js +++ b/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.service.js @@ -4,95 +4,44 @@ * All Rights Reserved *************************************************/ -export default -["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); - }); - } +export default ["Rest", "GetBasePath", "ProcessErrors", "$q", JobStatusGraphData]; +function JobStatusGraphData(Rest, getBasePath, processErrors, $q) { 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) { - - this.destroyWatcher(); - $timeout.cancel(this.refreshTimer); - this.refreshTimerRunning = false; - this.pendingRefresh = false; - this.setupWatcher(period, jobType); - - return 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); + }); } }; diff --git a/awx/ui/client/src/home/home.controller.js b/awx/ui/client/src/home/home.controller.js index 4af7adbb16..abebcd54b7 100644 --- a/awx/ui/client/src/home/home.controller.js +++ b/awx/ui/client/src/home/home.controller.js @@ -4,9 +4,9 @@ * All Rights Reserved *************************************************/ -export default ['$scope', '$rootScope','Wait', '$timeout', +export default ['$scope','Wait', '$timeout', 'i18n', 'Rest', 'GetBasePath', 'ProcessErrors', 'graphData', - function($scope, $rootScope, Wait, $timeout, + function($scope, Wait, $timeout, i18n, Rest, GetBasePath, ProcessErrors, graphData) { var dataCount = 0; @@ -53,16 +53,10 @@ export default ['$scope', '$rootScope','Wait', '$timeout', $scope.removeDashboardReady = $scope.$on('dashboardReady', function (e, data) { $scope.dashboardCountsData = data; $scope.graphData = graphData; + $scope.graphData.period = "month"; + $scope.graphData.jobType = "all"; + $scope.graphData.status = "both"; $scope.$emit('dashboardDataLoadComplete'); - - var cleanupJobListener = - $rootScope.$on('DataReceived:JobStatusGraph', function(e, data) { - $scope.graphData.jobStatus = data; - }); - - $scope.$on('$destroy', function() { - cleanupJobListener(); - }); }); if ($scope.removeDashboardJobsListReady) { @@ -81,40 +75,6 @@ export default ['$scope', '$rootScope','Wait', '$timeout', $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 () { Rest.setUrl(GetBasePath('dashboard')); Rest.get() @@ -122,7 +82,7 @@ export default ['$scope', '$rootScope','Wait', '$timeout', $scope.dashboardData = data; }) .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"); @@ -132,7 +92,7 @@ export default ['$scope', '$rootScope','Wait', '$timeout', $scope.dashboardJobsListData = data.results; }) .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"); @@ -141,8 +101,24 @@ export default ['$scope', '$rootScope','Wait', '$timeout', $scope.dashboardJobTemplatesListData = data.results; }) .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; refreshTimerRunning = true; $timeout(() => { @@ -154,5 +130,35 @@ export default ['$scope', '$rootScope','Wait', '$timeout', }, 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}`) }); + }); + } ]; diff --git a/awx/ui/client/src/home/home.route.js b/awx/ui/client/src/home/home.route.js index ffc9d2681a..3f6c657dd0 100644 --- a/awx/ui/client/src/home/home.route.js +++ b/awx/ui/client/src/home/home.route.js @@ -10,12 +10,7 @@ export default { params: { licenseMissing: null }, data: { activityStream: true, - refreshButton: true, - socket: { - "groups": { - "jobs": ["status_changed"] - } - }, + refreshButton: true }, ncyBreadcrumb: { label: N_("DASHBOARD") diff --git a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js index 0c69e900ad..8074e96d06 100644 --- a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js +++ b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js @@ -175,12 +175,7 @@ let lists = [{ }, data: { activityStream: true, - activityStreamTarget: 'organization', - socket: { - "groups": { - "jobs": ["status_changed"] - } - }, + activityStreamTarget: 'organization' }, ncyBreadcrumb: { parent: "organizations.edit", diff --git a/awx/ui/client/src/shared/socket/socket.service.js b/awx/ui/client/src/shared/socket/socket.service.js index b0a770f02f..c492bcb5e5 100644 --- a/awx/ui/client/src/shared/socket/socket.service.js +++ b/awx/ui/client/src/shared/socket/socket.service.js @@ -96,6 +96,14 @@ export default $log.debug('Received From Server: ' + e.data); 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"){ $log.debug('Message from server dropped: ' + e.data); needsRefreshAfterBlur = true; @@ -254,21 +262,23 @@ export default // requires a subscribe or an unsubscribe var self = this; return socketPromise.promise.then(function(){ - if(!state.data || !state.data.socket){ - _.merge(state.data, {socket: {groups: {}}}); - self.unsubscribe(state); + if (_.get(state, 'data.socket.groups.jobs')) { + if (!state.data.socket.groups.jobs.includes("status_changed")) { + state.data.socket.groups.jobs.push("status_changed"); + } } - else{ - ["job_events", "ad_hoc_command_events", "workflow_events", + else if(!state.data || !state.data.socket){ + _.merge(state.data, {socket: {groups: {jobs: ["status_changed"]}}}); + } + ["job_events", "ad_hoc_command_events", "workflow_events", "project_update_events", "inventory_update_events", "system_job_events" - ].forEach(function(group) { - if(state.data && state.data.socket && state.data.socket.groups.hasOwnProperty(group)){ - state.data.socket.groups[group] = [id]; - } - }); - self.subscribe(state); - } + ].forEach(function(group) { + if(state.data && state.data.socket && state.data.socket.groups.hasOwnProperty(group)){ + state.data.socket.groups[group] = [id]; + } + }); + self.subscribe(state); return true; }); } diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js index ea6f4eea01..323f13824c 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js @@ -608,10 +608,23 @@ export default ['$scope', 'TemplatesService', } } else if ($scope.nodeConfig.mode === "edit") { if (selectedTemplate) { - 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; + 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; + } 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.arrayOfLinksForChart.map( (link) => { @@ -622,16 +635,6 @@ export default ['$scope', 'TemplatesService', 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; } } diff --git a/awx/ui/client/src/workflow-results/workflow-results.route.js b/awx/ui/client/src/workflow-results/workflow-results.route.js index 85d81cc49d..149a211129 100644 --- a/awx/ui/client/src/workflow-results/workflow-results.route.js +++ b/awx/ui/client/src/workflow-results/workflow-results.route.js @@ -15,13 +15,6 @@ export default { parent: 'jobs', label: '{{ workflow.id }} - {{ workflow.name }}' }, - data: { - socket: { - "groups":{ - "jobs": ["status_changed"] - } - } - }, templateUrl: templateUrl('workflow-results/workflow-results'), controller: workflowResultsController, resolve: {