event processing for details panel and initial stats bar integration

This commit is contained in:
Jake McDermott
2018-03-19 12:01:17 -04:00
parent f65d170cab
commit b577f50930
12 changed files with 447 additions and 82 deletions

View File

@@ -199,3 +199,85 @@
width: 100%; width: 100%;
flex-wrap: wrap; flex-wrap: wrap;
} }
// Status Bar -----------------------------------------------------------------------------
.HostStatusBar {
display: flex;
flex: 0 0 auto;
width: 100%;
margin-top: 10px;
}
.HostStatusBar-ok,
.HostStatusBar-changed,
.HostStatusBar-unreachable,
.HostStatusBar-failed,
.HostStatusBar-skipped,
.HostStatusBar-noData {
height: 15px;
border-top: 5px solid @default-bg;
border-bottom: 5px solid @default-bg;
}
.HostStatusBar-ok {
background-color: @default-succ;
display: flex;
flex: 0 0 auto;
}
.HostStatusBar-changed {
background-color: @default-warning;
flex: 0 0 auto;
}
.HostStatusBar-unreachable {
background-color: @default-unreachable;
flex: 0 0 auto;
}
.HostStatusBar-failed {
background-color: @default-err;
flex: 0 0 auto;
}
.HostStatusBar-skipped {
background-color: @default-link;
flex: 0 0 auto;
}
.HostStatusBar-noData {
background-color: @default-icon-hov;
flex: 1 0 auto;
}
.HostStatusBar-tooltipLabel {
text-transform: uppercase;
margin-right: 15px;
}
.HostStatusBar-tooltipBadge {
border-radius: 5px;
border: 1px solid @default-bg;
}
.HostStatusBar-tooltipBadge--ok {
background-color: @default-succ;
}
.HostStatusBar-tooltipBadge--unreachable {
background-color: @default-unreachable;
}
.HostStatusBar-tooltipBadge--skipped {
background-color: @default-link;
}
.HostStatusBar-tooltipBadge--changed {
background-color: @default-warning;
}
.HostStatusBar-tooltipBadge--failed {
background-color: @default-err;
}

View File

@@ -2,6 +2,7 @@ const templateUrl = require('~features/output/details.partial.html');
let $http; let $http;
let $filter; let $filter;
let $scope;
let $state; let $state;
let error; let error;
@@ -12,68 +13,86 @@ let strings;
let wait; let wait;
function mapChoices (choices) { function mapChoices (choices) {
return Object.assign(...choices.map(([k, v]) => ({[k]: v}))); if (!choices) return {};
return Object.assign(...choices.map(([k, v]) => ({ [k]: v })));
} }
function getStatusDetails (status) { function getStatusDetails (status) {
const value = status || resource.model.get('status'); const unmapped = status || resource.model.get('status');
const label = 'Status';
if (!unmapped) {
return null;
}
const choices = mapChoices(resource.model.options('actions.GET.status.choices')); const choices = mapChoices(resource.model.options('actions.GET.status.choices'));
const displayValue = choices[value]; const label = 'Status';
const icon = `fa icon-job-${unmapped}`;
const value = choices[unmapped];
return { displayValue, label, value }; return { label, icon, value };
} }
function getStartTimeDetails (started) { function getStartTimeDetails (started) {
const value = started || resource.model.get('started'); const unfiltered = started || resource.model.get('started');
const label = 'Started'; const label = 'Started';
let displayValue; let value;
if (value) { if (unfiltered) {
displayValue = $filter('longDate')(value); value = $filter('longDate')(unfiltered);
} else { } else {
displayValue = 'Not Started'; value = 'Not Started';
} }
return { displayValue, label, value }; return { label, value };
} }
function getFinishTimeDetails (finished) { function getFinishTimeDetails (finished) {
const value = finished || resource.model.get('finished'); const unfiltered = finished || resource.model.get('finished');
const label = 'Finished'; const label = 'Finished';
let displayValue; let value;
if (value) { if (unfiltered) {
displayValue = $filter('longDate')(value); value = $filter('longDate')(unfiltered);
} else { } else {
displayValue = 'Not Finished'; value = 'Not Finished';
} }
return { displayValue, label, value }; return { label, value };
} }
function getJobTypeDetails () { function getJobTypeDetails () {
const value = resource.model.get('job_type'); const unmapped = resource.model.get('job_type');
const label = 'Job Type';
if (!unmapped) {
return null;
}
const choices = mapChoices(resource.model.options('actions.GET.job_type.choices')); const choices = mapChoices(resource.model.options('actions.GET.job_type.choices'));
const displayValue = choices[value]; const label = 'Job Type';
const value = choices[unmapped];
return { displayValue, label, value }; return { label, value };
} }
function getVerbosityDetails () { function getVerbosityDetails () {
const value = resource.model.get('verbosity'); const verbosity = resource.model.get('verbosity');
if (!verbosity) {
return null;
}
const choices = mapChoices(resource.model.options('actions.GET.verbosity.choices')); const choices = mapChoices(resource.model.options('actions.GET.verbosity.choices'));
const displayValue = choices[value];
const label = 'Verbosity'; const label = 'Verbosity';
const value = choices[value];
return { displayValue, label, value }; return { label, value };
} }
function getSourceWorkflowJobDetails () { function getSourceWorkflowJobDetails () {
@@ -273,7 +292,6 @@ function getLimitDetails () {
} }
function getInstanceGroupDetails () { function getInstanceGroupDetails () {
const instanceGroup = resource.model.get('summary_fields.instance_group'); const instanceGroup = resource.model.get('summary_fields.instance_group');
if (!instanceGroup) { if (!instanceGroup) {
@@ -336,9 +354,9 @@ function getLabelDetails () {
} }
const label = 'Labels'; const label = 'Labels';
const value = jobLabels.map(({ name }) => name).map($filter('sanitize')); const more = false;
let more = false; const value = jobLabels.map(({ name }) => name).map($filter('sanitize'));
return { label, more, value }; return { label, more, value };
} }
@@ -396,9 +414,7 @@ function cancelJob () {
prompt({ hdr, resourceName, body, actionText, action }); prompt({ hdr, resourceName, body, actionText, action });
} }
function deleteJob () { function deleteJob () {}
return;
}
function AtDetailsController ( function AtDetailsController (
_$http_, _$http_,
@@ -418,21 +434,18 @@ function AtDetailsController (
$state = _$state_; $state = _$state_;
error = _error_; error = _error_;
// resource = _resource_;
parse = ParseVariableString; parse = ParseVariableString;
prompt = _prompt_; prompt = _prompt_;
strings = _strings_; strings = _strings_;
wait = _wait_; wait = _wait_;
// statusChoices = mapChoices(resource.options('status.choices')); vm.init = _$scope_ => {
$scope = _$scope_;
resource = $scope.resource;
vm.init = scope => { vm.status = getStatusDetails();
vm.job = scope.job || {}; vm.started = getStartTimeDetails();
resource = scope.resource; vm.finished = getFinishTimeDetails();
vm.status = getStatusDetails(scope.status);
vm.startTime = getStartTimeDetails();
vm.finishTime = getFinishTimeDetails();
vm.jobType = getJobTypeDetails(); vm.jobType = getJobTypeDetails();
vm.jobTemplate = getJobTemplateDetails(); vm.jobTemplate = getJobTemplateDetails();
vm.sourceWorkflowJob = getSourceWorkflowJobDetails(); vm.sourceWorkflowJob = getSourceWorkflowJobDetails();
@@ -457,12 +470,24 @@ function AtDetailsController (
vm.deleteJob = deleteJob; vm.deleteJob = deleteJob;
vm.toggleLabels = toggleLabels; vm.toggleLabels = toggleLabels;
// codemirror const observe = (key, transform) => {
const cm = { parseType: 'yaml', variables: vm.extraVars.value, $apply: scope.$apply }; $scope.$watch(key, value => { this[key] = transform(value); });
ParseTypeChange({ scope: cm, field_id: 'cm-extra-vars', readOnly: true }); };
scope.$watch('status', value => { vm.status = getStatusDetails(value); }); observe('status', getStatusDetails);
} observe('started', getStartTimeDetails);
observe('finished', getFinishTimeDetails);
// relaunch component
$scope.job = _.get(resource.model, 'model.GET', {});
this.job = $scope.job;
// codemirror
if (this.extraVars) {
const cm = { parseType: 'yaml', variables: this.extraVars.value, $apply: $scope.$apply };
ParseTypeChange({ scope: cm, field_id: 'cm-extra-vars', readOnly: true });
}
};
} }
AtDetailsController.$inject = [ AtDetailsController.$inject = [
@@ -492,9 +517,10 @@ function atDetails () {
link: atDetailsLink, link: atDetailsLink,
controller: AtDetailsController, controller: AtDetailsController,
scope: { scope: {
job: '=',
status: '=',
resource: '=', resource: '=',
status: '=',
started: '=',
finished: '=',
}, },
}; };
} }

View File

@@ -36,25 +36,25 @@
<div class="JobResults-resultRow"> <div class="JobResults-resultRow">
<label class="JobResults-resultRowLabel">{{ vm.status.label}}</label> <label class="JobResults-resultRowLabel">{{ vm.status.label}}</label>
<div class="JobResults-resultRowText"> <div class="JobResults-resultRowText">
<i class="JobResults-statusResultIcon fa icon-job-{{ vm.status.value }}"></i> <i class="JobResults-statusResultIcon {{ vm.status.icon }}"></i>
{{ vm.status.displayValue | translate }} {{ vm.status.value }}
</div> </div>
</div> </div>
<!-- START TIME DETAIL --> <!-- START TIME DETAIL -->
<div class="JobResults-resultRow" ng-if="vm.startTime"> <div class="JobResults-resultRow" ng-if="vm.started">
<label class="JobResults-resultRowLabel">{{ vm.startTime.label }}</label> <label class="JobResults-resultRowLabel">{{ vm.started.label }}</label>
<div class="JobResults-resultRowText"> <div class="JobResults-resultRowText">
{{ vm.startTime.displayValue }} {{ vm.started.value }}
</div> </div>
</div> </div>
<!-- FINISHED TIME DETAIL --> <!-- FINISHED TIME DETAIL -->
<div class="JobResults-resultRow" ng-show="vm.startTime"> <div class="JobResults-resultRow" ng-show="vm.started">
<label class="JobResults-resultRowLabel">{{ vm.finishTime.label }}</label> <label class="JobResults-resultRowLabel">{{ vm.finished.label }}</label>
<div class="JobResults-resultRowText"> <div class="JobResults-resultRowText">
{{ vm.finishTime.displayValue }} {{ vm.finished.value }}
</div> </div>
</div> </div>
@@ -81,7 +81,7 @@
<!-- JOB TYPE DETAIL --> <!-- JOB TYPE DETAIL -->
<div class="JobResults-resultRow" ng-if="vm.jobType"> <div class="JobResults-resultRow" ng-if="vm.jobType">
<label class="JobResults-resultRowLabel">{{ vm.jobType.label }}</label> <label class="JobResults-resultRowLabel">{{ vm.jobType.label }}</label>
<div class="JobResults-resultRowText">{{ vm.jobType.displayValue }}</div> <div class="JobResults-resultRowText">{{ vm.jobType.value }}</div>
</div> </div>
<!-- LAUNCHED BY DETAIL --> <!-- LAUNCHED BY DETAIL -->
@@ -164,7 +164,7 @@
<!-- VERBOSITY DETAIL --> <!-- VERBOSITY DETAIL -->
<div class="JobResults-resultRow" ng-if="vm.verbosity"> <div class="JobResults-resultRow" ng-if="vm.verbosity">
<label class="JobResults-resultRowLabel">{{ vm.verbosity.label }}</label> <label class="JobResults-resultRowLabel">{{ vm.verbosity.label }}</label>
<div class="JobResults-resultRowText">{{ vm.verbosity.displayValue }}</div> <div class="JobResults-resultRowText">{{ vm.verbosity.value }}</div>
</div> </div>
<!-- IG DETAIL --> <!-- IG DETAIL -->

View File

@@ -10,6 +10,8 @@ let resource;
let $state; let $state;
let qs; let qs;
let hack;
function JobsIndexController ( function JobsIndexController (
_resource_, _resource_,
_page_, _page_,
@@ -51,7 +53,7 @@ function JobsIndexController (
vm.expand = expand; vm.expand = expand;
vm.isExpanded = true; vm.isExpanded = true;
// search // Search
$state = _$state_; $state = _$state_;
qs = _qs_; qs = _qs_;
@@ -67,17 +69,47 @@ function JobsIndexController (
vm.removeSearchTag = removeSearchTag; vm.removeSearchTag = removeSearchTag;
vm.searchTags = getSearchTags(getCurrentQueryset()); vm.searchTags = getSearchTags(getCurrentQueryset());
// details // Host Status Bar
vm.status = {
running: Boolean(resource.model.get('started')) && !resource.model.get('finished'),
stats: resource.stats,
}
// Details
vm.details = { vm.details = {
job: resource.model.model.GET,
status: resource.model.model.GET.status,
resource, resource,
started: resource.model.get('started'),
finished: resource.model.get('finished'),
status: resource.model.get('status'),
}; };
render.requestAnimationFrame(() => init()); render.requestAnimationFrame(() => init());
} }
function onStreamStart (data) {
const status = _.get(data, 'summary_fields.job.status');
if (!hack) {
hack = true;
vm.details.status = status;
vm.details.started = data.created;
vm.status.running = true;
}
}
function onStreamFinish (data) {
const failed = _.get(data, 'summary_fields.job.failed');
vm.details.status = failed ? 'failed' : 'successful';
vm.details.finished = data.created;
vm.status = { stats: data, running: false };
};
function init (pageMode) { function init (pageMode) {
hack = false;
page.init({ page.init({
resource, resource,
}); });
@@ -98,10 +130,12 @@ function init (pageMode) {
page, page,
scroll, scroll,
resource, resource,
onStreamStart,
onStreamFinish,
render: events => shift().then(() => append(events, true)), render: events => shift().then(() => append(events, true)),
listen: (namespace, listener) => { listen: (namespace, listener) => {
$scope.$on(namespace, (scope, data) => listener(data)); $scope.$on(namespace, (scope, data) => listener(data));
} },
}); });
if (pageMode) { if (pageMode) {

View File

@@ -6,13 +6,16 @@ import Controller from '~features/output/index.controller';
import PageService from '~features/output/page.service'; import PageService from '~features/output/page.service';
import RenderService from '~features/output/render.service'; import RenderService from '~features/output/render.service';
import ScrollService from '~features/output/scroll.service'; import ScrollService from '~features/output/scroll.service';
import SearchKeyDirective from '~features/output/search-key.directive';
import StreamService from '~features/output/stream.service'; import StreamService from '~features/output/stream.service';
import DetailsDirective from '~features/output/details.directive.js';
import DetailsDirective from '~features/output/details.directive';
import SearchKeyDirective from '~features/output/search-key.directive';
import StatusDirective from '~features/output/status.directive';
const Template = require('~features/output/index.view.html'); const Template = require('~features/output/index.view.html');
const MODULE_NAME = 'at.features.output'; const MODULE_NAME = 'at.features.output';
const PAGE_CACHE = true; const PAGE_CACHE = true;
const PAGE_LIMIT = 5; const PAGE_LIMIT = 5;
const PAGE_SIZE = 50; const PAGE_SIZE = 50;
@@ -66,13 +69,21 @@ function resolveResource (
Wait('start'); Wait('start');
return new Resource(['get', 'options'], [id, id]) return new Resource(['get', 'options'], [id, id])
.then(model => Promise.all([ .then(model => {
model.extend('labels'), const promises = [model.getStats()];
model.extend(related, config)
])) if (model.has('related.labels')) {
.then(([ model ]) => ({ promises.push(model.extend('labels'));
}
promises.push(model.extend(related, config));
return Promise.all(promises);
})
.then(([stats, model]) => ({
id, id,
type, type,
stats,
model, model,
related, related,
ws: { ws: {
@@ -200,6 +211,7 @@ angular
.service('JobStreamService', StreamService) .service('JobStreamService', StreamService)
.directive('atDetails', DetailsDirective) .directive('atDetails', DetailsDirective)
.directive('atSearchKey', SearchKeyDirective) .directive('atSearchKey', SearchKeyDirective)
.directive('atStatus', StatusDirective)
.run(JobsRun); .run(JobsRun);
export default MODULE_NAME; export default MODULE_NAME;

View File

@@ -1,14 +1,20 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="col-md-4"> <div class="col-md-4">
<at-panel> <at-panel>
<at-details job="vm.details.job" status="vm.details.status" resource="vm.details.resource"></at-details> <at-details
<p><button class="btn" ng-click="vm.clear(true)">Page Mode</button></p> resource="vm.details.resource"
status="vm.details.status"
started="vm.details.started"
finished="vm.details.finished">
</at-details>
<!-- --><p><button class="btn" ng-click="vm.clear(true)">Page Mode</button></p>
</at-panel> </at-panel>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<at-panel class="at-Stdout"> <at-panel class="at-Stdout">
<!-- search ============================================================================================================= --> <at-status running="vm.status.running" stats="vm.status.stats"></at-status>
<!-- search ===================================================================================== -->
<form ng-submit="vm.search()"> <form ng-submit="vm.search()">
<div class="input-group"> <div class="input-group">
<input type="text" <input type="text"
@@ -52,7 +58,7 @@
<at-search-key ng-show="vm.searchKey" fields="vm.searchKeyFields" examples="vm.searchKeyExamples"></at-search-key> <at-search-key ng-show="vm.searchKey" fields="vm.searchKeyFields" examples="vm.searchKeyExamples"></at-search-key>
<!-- ==================================================================================================================== --> <!-- ============================================================================================ -->
<div class="at-Stdout-menuTop"> <div class="at-Stdout-menuTop">
<div class="pull-left" ng-click="vm.expand()"> <div class="pull-left" ng-click="vm.expand()">
<i class="at-Stdout-menuIcon fa" <i class="at-Stdout-menuIcon fa"

View File

@@ -12,6 +12,11 @@ function JobsStrings (BaseString) {
CANCEL_BODY: t.s('Are you sure you want to cancel this job?'), CANCEL_BODY: t.s('Are you sure you want to cancel this job?'),
CANCEL_HEADER: t.s('Cancel Job'), CANCEL_HEADER: t.s('Cancel Job'),
}; };
ns.status = {
RUNNING: t.s('The host status bar will update when the job is complete.'),
UNAVAILABLE: t.s('Host status information for this job unavailable.'),
};
} }
JobsStrings.$inject = ['BaseStringService']; JobsStrings.$inject = ['BaseStringService'];

View File

@@ -0,0 +1,92 @@
const templateUrl = require('~features/output/status.partial.html');
const HOST_STATUS_KEYS = ['dark', 'failures', 'changed', 'ok', 'skipped'];
function getHostStatusCounts (statsEvent) {
const countedHostNames = [];
const counts = Object.assign(...HOST_STATUS_KEYS.map(key => ({ [key]: 0 })));
HOST_STATUS_KEYS.forEach(key => {
const hostData = _.get(statsEvent, ['event_data', key], {});
Object.keys(hostData).forEach(hostName => {
const isAlreadyCounted = (countedHostNames.indexOf(hostName) > -1);
const shouldBeCounted = ((!isAlreadyCounted) && hostData[hostName] > 0);
if (shouldBeCounted) {
countedHostNames.push(hostName);
counts[key]++;
}
});
});
return counts;
}
function createStatusBarTooltip (key, count) {
const label = `<span class='HostStatusBar-tooltipLabel'>${key}</span>`;
const badge = `<span class='badge HostStatusBar-tooltipBadge HostStatusBar-tooltipBadge--${key}'>${count}</span>`;
return `${label}${badge}`;
}
function atStatusLink (scope, el, attrs, controllers) {
const [atStatusController] = controllers;
atStatusController.init(scope);
}
function AtStatusController (strings) {
const vm = this || {};
vm.tooltips = {
running: strings.get('status.RUNNING'),
unavailable: strings.get('status.UNAVAILABLE'),
};
vm.init = scope => {
const { running, stats } = scope;
vm.running = running || false;
vm.setStats(stats);
scope.$watch('running', value => { vm.running = value; });
scope.$watch('stats', vm.setStats);
};
vm.setStats = stats => {
const counts = getHostStatusCounts(stats);
HOST_STATUS_KEYS.forEach(key => {
const count = counts[key];
const statusBarElement = $(`.HostStatusBar-${key}`);
statusBarElement.css('flex', `${count} 0 auto`);
vm.tooltips[key] = createStatusBarTooltip(key, count);
});
vm.statsAreAvailable = Boolean(stats);
};
}
function atStatus () {
return {
templateUrl,
restrict: 'E',
require: ['atStatus'],
controllerAs: 'vm',
link: atStatusLink,
controller: [
'JobStrings',
AtStatusController
],
scope: {
running: '=',
stats: '=',
},
};
}
export default atStatus;

View File

@@ -0,0 +1,37 @@
<div class="HostStatusBar">
<div class="HostStatusBar-ok"
data-placement="top"
aw-tool-tip="{{ vm.tooltips.ok }}"
data-tip-watch="vm.tooltips.ok">
</div>
<div class="HostStatusBar-skipped"
data-placement="top"
aw-tool-tip="{{ vm.tooltips.skipped }}"
data-tip-watch="vm.tooltips.skipped">
</div>
<div class="HostStatusBar-changed"
data-placement="top"
aw-tool-tip="{{ vm.tooltips.changed }}"
data-tip-watch="vm.tooltips.changed">
</div>
<div class="HostStatusBar-failures"
data-placement="top"
aw-tool-tip="{{ vm.tooltips.failures }}"
data-tip-watch="vm.tooltips.failures">
</div>
<div class="HostStatusBar-unreachable"
data-placement="top"
aw-tool-tip="{{ vm.tooltips.unreachable }}"
data-tip-watch="vm.tooltips.unreachable">
</div>
<div class="HostStatusBar-noData"
ng-show="vm.running"
data-placement="top"
aw-tool-tip="{{:: vm.tooltips.running }}">
</div>
<div class="HostStatusBar-noData"
ng-show="!vm.running && !vm.statsAreAvailable"
data-placement="top"
aw-tool-tip="{{:: vm.tooltips.unavailable }}">
</div>
</div>

View File

@@ -3,7 +3,7 @@ const JOB_END = 'playbook_on_stats';
const MAX_LAG = 120; const MAX_LAG = 120;
function JobStreamService ($q) { function JobStreamService ($q) {
this.init = ({ resource, scroll, page, render, listen }) => { this.init = ({ resource, scroll, page, onStreamStart, onStreamFinish, render, listen }) => {
this.resource = resource; this.resource = resource;
this.scroll = scroll; this.scroll = scroll;
this.page = page; this.page = page;
@@ -23,8 +23,10 @@ function JobStreamService ($q) {
}; };
this.hooks = { this.hooks = {
onStreamStart,
onStreamFinish,
render, render,
listen listen,
}; };
this.lines = { this.lines = {
@@ -35,7 +37,7 @@ function JobStreamService ($q) {
max: 0 max: 0
}; };
this.hooks.listen(resource.ws.namespace, this.listen); this.hooks.listen(resource.ws.namespace, this.listener);
}; };
this.getBatchFactors = size => { this.getBatchFactors = size => {
@@ -105,19 +107,25 @@ function JobStreamService ($q) {
} }
}; };
this.listen = data => { this.listener = data => {
this.lag++; this.lag++;
this.chain = this.chain this.chain = this.chain
.then(() => { .then(() => {
// console.log(data);
if (!this.isActive()) { if (!this.isActive()) {
this.start(); this.start();
if (!this.isEnding()) {
this.hooks.onStreamStart(data);
}
} else if (data.event === JOB_END) { } else if (data.event === JOB_END) {
if (this.isPaused()) { if (this.isPaused()) {
this.end(true); this.end(true);
} else { } else {
this.end(); this.end();
} }
this.hooks.onStreamFinish(data);
} }
this.checkLines(data); this.checkLines(data);

View File

@@ -23,26 +23,54 @@ function postRelaunch (params) {
return $http(req); return $http(req);
} }
function getStats () {
if (!this.has('GET', 'id')) {
return Promise.reject(new Error('No property, id, exists'));
}
if (!this.has('GET', 'related.job_events')) {
return Promise.reject(new Error('No related property, job_events, exists'));
}
const req = {
method: 'GET',
url: `${this.path}${this.get('id')}/job_events/`,
params: { event: 'playbook_on_stats' },
};
return $http(req)
.then(({ data }) => {
if (data.results.length > 0) {
return data.results[0];
}
return null;
});
}
function JobModel (method, resource, config) { function JobModel (method, resource, config) {
BaseModel.call(this, 'jobs'); BaseModel.call(this, 'jobs');
this.Constructor = JobModel; this.Constructor = JobModel;
this.postRelaunch = postRelaunch.bind(this); this.postRelaunch = postRelaunch.bind(this);
this.getRelaunch = getRelaunch.bind(this); this.getRelaunch = getRelaunch.bind(this);
this.getStats = getStats.bind(this);
return this.create(method, resource, config); return this.create(method, resource, config);
} }
function JobModelLoader (_BaseModel_, _$http_) { function JobModelLoader (_$http_, _BaseModel_) {
BaseModel = _BaseModel_;
$http = _$http_; $http = _$http_;
BaseModel = _BaseModel_;
return JobModel; return JobModel;
} }
JobModelLoader.$inject = [ JobModelLoader.$inject = [
'$http',
'BaseModel', 'BaseModel',
'$http'
]; ];
export default JobModelLoader; export default JobModelLoader;

View File

@@ -1,19 +1,54 @@
let $http;
let BaseModel; let BaseModel;
function getStats () {
if (!this.has('GET', 'id')) {
return Promise.reject(new Error('No property, id, exists'));
}
if (!this.has('GET', 'related.events')) {
return Promise.reject(new Error('No related property, events, exists'));
}
const req = {
method: 'GET',
url: `${this.path}${this.get('id')}/events/`,
params: { event: 'playbook_on_stats' },
};
return $http(req)
.then(({ data }) => {
console.log(data);
if (data.results.length > 0) {
return data.results[0];
}
return null;
})
}
function ProjectUpdateModel (method, resource, config) { function ProjectUpdateModel (method, resource, config) {
BaseModel.call(this, 'project_updates'); BaseModel.call(this, 'project_updates');
this.getStats = getStats;
this.Constructor = ProjectUpdateModel; this.Constructor = ProjectUpdateModel;
return this.create(method, resource, config); return this.create(method, resource, config);
} }
function ProjectUpdateModelLoader (_BaseModel_) { function ProjectUpdateModelLoader (_$http_, _BaseModel_) {
$http = _$http_;
BaseModel = _BaseModel_; BaseModel = _BaseModel_;
return ProjectUpdateModel; return ProjectUpdateModel;
} }
ProjectUpdateModelLoader.$inject = ['BaseModel']; ProjectUpdateModelLoader.$inject = [
'$http',
'BaseModel'
];
export default ProjectUpdateModelLoader; export default ProjectUpdateModelLoader;