diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index f13de2821b..510173d9ad 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -10,7 +10,6 @@ let parse; let prompt; let resource; let strings; -let status; let wait; let vm; @@ -553,9 +552,9 @@ function AtJobDetailsController ( _error_, _prompt_, _strings_, - _status_, _wait_, _parse_, + { subscribe } ) { vm = this || {}; @@ -567,7 +566,6 @@ function AtJobDetailsController ( parse = _parse_; prompt = _prompt_; strings = _strings_; - status = _status_; wait = _wait_; vm.init = _$scope_ => { @@ -610,18 +608,13 @@ function AtJobDetailsController ( vm.toggleJobTags = toggleJobTags; vm.toggleSkipTags = toggleSkipTags; - const observe = (getter, transform, key) => { - $scope.$watch(getter, value => { vm[key] = transform(value); }); - }; - - observe(status.getStarted, getStartDetails, 'started'); - observe(status.getFinished, getFinishDetails, 'finished'); - observe(status.getProjectUpdateId, getProjectUpdateDetails, 'projectUpdate'); - observe(status.getProjectStatus, getProjectStatusDetails, 'projectStatus'); - - $scope.$watch(status.getJobStatus, jobStatus => { - vm.status = getStatusDetails(jobStatus); - vm.job.status = jobStatus; + subscribe(({ status, started, finished, scm }) => { + vm.job.status = status; + vm.status = getStatusDetails(status); + vm.started = getStartDetails(started); + vm.finished = getFinishDetails(finished); + vm.projectUpdate = getProjectUpdateDetails(scm.id); + vm.projectStatus = getProjectStatusDetails(scm.status); }); }; } @@ -633,9 +626,9 @@ AtJobDetailsController.$inject = [ 'ProcessErrors', 'Prompt', 'JobStrings', - 'JobStatusService', 'Wait', 'ParseVariableString', + 'JobStatusService', ]; function atJobDetailsLink (scope, el, attrs, controllers) { diff --git a/awx/ui/client/features/output/search.directive.js b/awx/ui/client/features/output/search.directive.js index 0a688f92bb..6259013200 100644 --- a/awx/ui/client/features/output/search.directive.js +++ b/awx/ui/client/features/output/search.directive.js @@ -8,7 +8,6 @@ const PLACEHOLDER_RUNNING = 'CANNOT SEARCH RUNNING JOB'; const PLACEHOLDER_DEFAULT = 'SEARCH'; let $state; -let status; let qs; let vm; @@ -71,9 +70,8 @@ function atJobSearchLink (scope, el, attrs, controllers) { atJobSearchController.init(scope); } -function AtJobSearchController (_$state_, _status_, _qs_) { +function AtJobSearchController (_$state_, _qs_, { subscribe }) { $state = _$state_; - status = _status_; qs = _qs_; vm = this || {}; @@ -97,17 +95,17 @@ function AtJobSearchController (_$state_, _status_, _qs_) { vm.placeholder = PLACEHOLDER_DEFAULT; vm.relatedFields = scope.relatedFields || []; - scope.$watch(status.isRunning, value => { - vm.disabled = value; - vm.placeholder = value ? PLACEHOLDER_RUNNING : PLACEHOLDER_DEFAULT; + subscribe(({ running }) => { + vm.disabled = running; + vm.placeholder = running ? PLACEHOLDER_RUNNING : PLACEHOLDER_DEFAULT; }); }; } AtJobSearchController.$inject = [ '$state', - 'JobStatusService', 'QuerySet', + 'JobStatusService', ]; function atJobSearch () { diff --git a/awx/ui/client/features/output/stats.directive.js b/awx/ui/client/features/output/stats.directive.js index 789fc29de6..182ab27479 100644 --- a/awx/ui/client/features/output/stats.directive.js +++ b/awx/ui/client/features/output/stats.directive.js @@ -1,6 +1,5 @@ const templateUrl = require('~features/output/stats.partial.html'); -let status; let strings; function createStatsBarTooltip (key, count) { @@ -16,8 +15,7 @@ function atJobStatsLink (scope, el, attrs, controllers) { atJobStatsController.init(scope); } -function AtJobStatsController (_strings_, _status_) { - status = _status_; +function AtJobStatsController (_strings_, { subscribe }) { strings = _strings_; const vm = this || {}; @@ -36,18 +34,17 @@ function AtJobStatsController (_strings_, _status_) { vm.toggleStdoutFullscreenTooltip = strings.get('expandCollapse.EXPAND'); - vm.setHostStatusCounts(status.getHostStatusCounts()); - - scope.$watch(status.getPlayCount, value => { vm.plays = value; }); - scope.$watch(status.getTaskCount, value => { vm.tasks = value; }); - scope.$watch(status.getElapsed, value => { vm.elapsed = value; }); - scope.$watch(status.getHostCount, value => { vm.hosts = value; }); - scope.$watch(status.isRunning, value => { vm.running = value; }); - - scope.$watchCollection(status.getHostStatusCounts, vm.setHostStatusCounts); + subscribe(({ running, elapsed, counts, stats, hosts }) => { + vm.plays = counts.plays; + vm.tasks = counts.tasks; + vm.hosts = counts.hosts; + vm.elapsed = elapsed; + vm.running = running; + vm.setHostStatusCounts(stats, hosts); + }); }; - vm.setHostStatusCounts = counts => { + vm.setHostStatusCounts = (stats, counts) => { Object.keys(counts).forEach(key => { const count = counts[key]; const statusBarElement = $(`.HostStatusBar-${key}`); @@ -57,7 +54,7 @@ function AtJobStatsController (_strings_, _status_) { vm.tooltips[key] = createStatsBarTooltip(key, count); }); - vm.statsAreAvailable = Boolean(status.getStatsEvent()); + vm.statsAreAvailable = stats; }; vm.toggleFullscreen = () => { @@ -78,7 +75,6 @@ function atJobStats () { controller: [ 'JobStrings', 'JobStatusService', - '$scope', AtJobStatsController ], scope: { diff --git a/awx/ui/client/features/output/status.service.js b/awx/ui/client/features/output/status.service.js index 638d2ff399..12e3fd2544 100644 --- a/awx/ui/client/features/output/status.service.js +++ b/awx/ui/client/features/output/status.service.js @@ -6,34 +6,46 @@ const TASK_START = 'playbook_on_task_start'; const HOST_STATUS_KEYS = ['dark', 'failures', 'changed', 'ok', 'skipped']; const FINISHED = ['successful', 'failed', 'error']; -let moment; - -function JobStatusService (_moment_) { - moment = _moment_; +function JobStatusService (moment, message) { + this.dispatch = () => message.dispatch('status', this.state); + this.subscribe = listener => message.subscribe('status', listener); this.init = ({ resource }) => { + const { model } = resource; + + this.created = model.get('created'); + this.job = model.get('id'); + this.jobType = model.get('type'); + this.project = model.get('project'); + + this.active = false; + this.latestTime = null; this.counter = -1; - this.created = resource.model.get('created'); - this.job = resource.model.get('id'); - this.jobType = resource.model.get('type'); - this.project = resource.model.get('project'); - this.elapsed = resource.model.get('elapsed'); - this.started = resource.model.get('started'); - this.finished = resource.model.get('finished'); - this.jobStatus = resource.model.get('status'); - this.projectStatus = resource.model.get('summary_fields.project_update.status'); - this.projectUpdateId = resource.model.get('summary_fields.project_update.id'); + this.state = { + running: false, + stats: false, + counts: { + plays: null, + tasks: null, + hosts: null, + }, + hosts: {}, + status: model.get('status'), + elapsed: model.get('elapsed'), + started: model.get('started'), + finished: model.get('finished'), + scm: { + id: model.get('summary_fields.project_update.id'), + status: model.get('summary_fields.project_update.status') + }, + }; - this.latestTime = null; - this.playCount = null; - this.taskCount = null; - this.hostCount = null; - this.active = false; - this.hostStatusCounts = {}; - - this.statsEvent = resource.stats; + this.setStatsEvent(resource.stats); this.updateStats(); + this.updateRunningState(); + + this.dispatch(); }; this.pushStatusEvent = data => { @@ -59,24 +71,46 @@ function JobStatusService (_moment_) { if (isLatest) { this.counter = data.counter; this.latestTime = data.created; - this.elapsed = moment(data.created).diff(this.created, 'seconds'); + this.setElapsed(moment(data.created).diff(this.created, 'seconds')); } if (data.event === JOB_START) { - this.started = this.started || data.created; + this.setStarted(this.state.started || data.created); } if (data.event === PLAY_START) { - this.playCount++; + this.state.counts.plays++; } if (data.event === TASK_START) { - this.taskCount++; + this.state.counts.tasks++; } if (data.event === JOB_END) { - this.statsEvent = data; + this.setStatsEvent(data); } + + this.dispatch(); + }; + + this.isExpectingStatsEvent = () => (this.jobType === 'job') || + (this.jobType === 'project_update'); + + this.updateStats = () => { + this.updateHostCounts(); + + if (this.statsEvent) { + this.state.stats = true; + this.setFinished(this.statsEvent.created); + this.setJobStatus(this.statsEvent.failed ? 'failed' : 'successful'); + } + }; + + this.updateRunningState = () => { + this.state.running = (Boolean(this.state.started) && !this.state.finished) || + (this.state.status === 'running') || + (this.state.status === 'pending') || + (this.state.status === 'waiting'); }; this.updateHostCounts = () => { @@ -98,74 +132,66 @@ function JobStatusService (_moment_) { }); }); - this.hostCount = countedHostNames.length; - this.hostStatusCounts = counts; + this.state.counts.hosts = countedHostNames.length; + this.setHostStatusCounts(counts); }; - this.updateStats = () => { - this.updateHostCounts(); - - if (this.statsEvent) { - this.setFinished(this.statsEvent.created); - this.setJobStatus(this.statsEvent.failed ? 'failed' : 'successful'); - } - }; - - this.isRunning = () => (Boolean(this.started) && !this.finished) || - (this.jobStatus === 'running') || - (this.jobStatus === 'pending') || - (this.jobStatus === 'waiting'); - - this.isExpectingStatsEvent = () => (this.jobType === 'job') || - (this.jobType === 'project_update'); - - this.getPlayCount = () => this.playCount; - this.getTaskCount = () => this.taskCount; - this.getHostCount = () => this.hostCount; - this.getHostStatusCounts = () => this.hostStatusCounts || {}; - this.getJobStatus = () => this.jobStatus; - this.getProjectStatus = () => this.projectStatus; - this.getProjectUpdateId = () => this.projectUpdateId; - this.getElapsed = () => this.elapsed; - this.getStatsEvent = () => this.statsEvent; - this.getStarted = () => this.started; - this.getFinished = () => this.finished; - this.setJobStatus = status => { - this.jobStatus = status; + this.state.status = status; if (!this.isExpectingStatsEvent() && _.includes(FINISHED, status)) { if (this.latestTime) { this.setFinished(this.latestTime); - - if (!this.started && this.elapsed) { - this.started = moment(this.latestTime).subtract(this.elapsed, 'seconds'); + if (!this.state.started && this.state.elapsed) { + this.setStarted(moment(this.latestTime) + .subtract(this.state.elapsed, 'seconds')); } } } + + this.updateRunningState(); + }; + + this.setElapsed = elapsed => { + this.state.elapsed = elapsed; + }; + + this.setStarted = started => { + this.state.started = started; + this.updateRunningState(); }; this.setProjectStatus = status => { - this.projectStatus = status; + this.state.scm.status = status; }; this.setProjectUpdateId = id => { - this.projectUpdateId = id; + this.state.scm.id = id; }; this.setFinished = time => { - this.finished = time; + this.state.finished = time; + this.updateRunningState(); + }; + + this.setStatsEvent = data => { + this.statsEvent = data; + }; + + this.setHostStatusCounts = counts => { + this.state.hosts = counts; }; this.resetCounts = () => { - this.playCount = 0; - this.taskCount = 0; - this.hostCount = 0; + this.state.counts.plays = 0; + this.state.counts.tasks = 0; + this.state.counts.hosts = 0; }; } JobStatusService.$inject = [ 'moment', + 'JobMessageService', ]; export default JobStatusService;