From e7796ccaee20846a25a2477c257f9a9cde5d901f Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Thu, 8 Feb 2018 15:02:48 -0800 Subject: [PATCH] Refactor job summary to a relationship Now that blocking queries are going to be in play, We can no longer pretend the two requests are one, since they have independent nomad indices. --- ui/app/adapters/job.js | 28 ++++++-------------- ui/app/models/job-summary.js | 39 +++++++++++++++++++++++++++ ui/app/models/job.js | 44 +++++++++++-------------------- ui/app/models/task-group.js | 6 +++-- ui/app/serializers/job-summary.js | 32 ++++++++++++++++++++++ ui/app/serializers/job.js | 26 ++++-------------- 6 files changed, 103 insertions(+), 72 deletions(-) create mode 100644 ui/app/models/job-summary.js create mode 100644 ui/app/serializers/job-summary.js diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index c783c6ff2..7f715c25e 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -1,5 +1,4 @@ import { inject as service } from '@ember/service'; -import RSVP from 'rsvp'; import { assign } from '@ember/polyfills'; import ApplicationAdapter from './application'; @@ -26,28 +25,17 @@ export default ApplicationAdapter.extend({ }); }, - findRecord(store, { modelName }, id, snapshot) { - // To make a findRecord response reflect the findMany response, the JobSummary - // from /summary needs to be stitched into the response. + findRecordSummary(modelName, name, snapshot, namespaceQuery) { + return this.ajax(`${this.buildURL(modelName, name, snapshot, 'findRecord')}/summary`, 'GET', { + data: assign(this.buildQuery() || {}, namespaceQuery), + }); + }, - // URL is the form of /job/:name?namespace=:namespace with arbitrary additional query params + findRecord(store, type, id, snapshot) { const [name, namespace] = JSON.parse(id); const namespaceQuery = namespace && namespace !== 'default' ? { namespace } : {}; - return RSVP.hash({ - job: this.ajax(this.buildURL(modelName, name, snapshot, 'findRecord'), 'GET', { - data: assign(this.buildQuery() || {}, namespaceQuery), - }), - summary: this.ajax( - `${this.buildURL(modelName, name, snapshot, 'findRecord')}/summary`, - 'GET', - { - data: assign(this.buildQuery() || {}, namespaceQuery), - } - ), - }).then(({ job, summary }) => { - job.JobSummary = summary; - return job; - }); + + return this._super(store, type, name, snapshot, namespaceQuery); }, findAllocations(job) { diff --git a/ui/app/models/job-summary.js b/ui/app/models/job-summary.js new file mode 100644 index 000000000..a9584aced --- /dev/null +++ b/ui/app/models/job-summary.js @@ -0,0 +1,39 @@ +import { collect, sum } from '@ember/object/computed'; +import Model from 'ember-data/model'; +import attr from 'ember-data/attr'; +import { belongsTo } from 'ember-data/relationships'; +import { fragmentArray } from 'ember-data-model-fragments/attributes'; +import sumAggregation from '../utils/properties/sum-aggregation'; + +export default Model.extend({ + job: belongsTo('job'), + + taskGroupSummaries: fragmentArray('task-group-summary'), + + // Aggregate allocation counts across all summaries + queuedAllocs: sumAggregation('taskGroupSummaries', 'queuedAllocs'), + startingAllocs: sumAggregation('taskGroupSummaries', 'startingAllocs'), + runningAllocs: sumAggregation('taskGroupSummaries', 'runningAllocs'), + completeAllocs: sumAggregation('taskGroupSummaries', 'completeAllocs'), + failedAllocs: sumAggregation('taskGroupSummaries', 'failedAllocs'), + lostAllocs: sumAggregation('taskGroupSummaries', 'lostAllocs'), + + allocsList: collect( + 'queuedAllocs', + 'startingAllocs', + 'runningAllocs', + 'completeAllocs', + 'failedAllocs', + 'lostAllocs' + ), + + totalAllocs: sum('allocsList'), + + pendingChildren: attr('number'), + runningChildren: attr('number'), + deadChildren: attr('number'), + + childrenList: collect('pendingChildren', 'runningChildren', 'deadChildren'), + + totalChildren: sum('childrenList'), +}); diff --git a/ui/app/models/job.js b/ui/app/models/job.js index b77fc7a66..ac536db04 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -1,10 +1,9 @@ -import { collect, sum, bool, equal, or } from '@ember/object/computed'; +import { alias, bool, equal, or } from '@ember/object/computed'; import { computed } from '@ember/object'; import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { belongsTo, hasMany } from 'ember-data/relationships'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; -import sumAggregation from '../utils/properties/sum-aggregation'; const JOB_TYPES = ['service', 'batch', 'system']; @@ -83,34 +82,21 @@ export default Model.extend({ datacenters: attr(), taskGroups: fragmentArray('task-group', { defaultValue: () => [] }), - taskGroupSummaries: fragmentArray('task-group-summary'), + summary: belongsTo('job-summary'), - // Aggregate allocation counts across all summaries - queuedAllocs: sumAggregation('taskGroupSummaries', 'queuedAllocs'), - startingAllocs: sumAggregation('taskGroupSummaries', 'startingAllocs'), - runningAllocs: sumAggregation('taskGroupSummaries', 'runningAllocs'), - completeAllocs: sumAggregation('taskGroupSummaries', 'completeAllocs'), - failedAllocs: sumAggregation('taskGroupSummaries', 'failedAllocs'), - lostAllocs: sumAggregation('taskGroupSummaries', 'lostAllocs'), - - allocsList: collect( - 'queuedAllocs', - 'startingAllocs', - 'runningAllocs', - 'completeAllocs', - 'failedAllocs', - 'lostAllocs' - ), - - totalAllocs: sum('allocsList'), - - pendingChildren: attr('number'), - runningChildren: attr('number'), - deadChildren: attr('number'), - - childrenList: collect('pendingChildren', 'runningChildren', 'deadChildren'), - - totalChildren: sum('childrenList'), + // Alias through to the summary, as if there was no relationship + taskGroupSummaries: alias('summary.taskGroupSummaries'), + queuedAllocs: alias('summary.queuedAllocs'), + startingAllocs: alias('summary.startingAllocs'), + runningAllocs: alias('summary.runningAllocs'), + completeAllocs: alias('summary.completeAllocs'), + failedAllocs: alias('summary.failedAllocs'), + lostAllocs: alias('summary.lostAllocs'), + totalAllocs: alias('summary.totalAllocs'), + pendingChildren: alias('summary.pendingChildren'), + runningChildren: alias('summary.runningChildren'), + deadChildren: alias('summary.deadChildren'), + totalChildren: alias('summary.childrenList'), version: attr('number'), diff --git a/ui/app/models/task-group.js b/ui/app/models/task-group.js index e5ea67d0e..9be65bdba 100644 --- a/ui/app/models/task-group.js +++ b/ui/app/models/task-group.js @@ -4,6 +4,8 @@ import attr from 'ember-data/attr'; import { fragmentOwner, fragmentArray } from 'ember-data-model-fragments/attributes'; import sumAggregation from '../utils/properties/sum-aggregation'; +const maybe = arr => arr || []; + export default Fragment.extend({ job: fragmentOwner(), @@ -13,7 +15,7 @@ export default Fragment.extend({ tasks: fragmentArray('task'), allocations: computed('job.allocations.@each.taskGroup', function() { - return this.get('job.allocations').filterBy('taskGroupName', this.get('name')); + return maybe(this.get('job.allocations')).filterBy('taskGroupName', this.get('name')); }), reservedCPU: sumAggregation('tasks', 'reservedCPU'), @@ -32,6 +34,6 @@ export default Fragment.extend({ }), summary: computed('job.taskGroupSummaries.[]', function() { - return this.get('job.taskGroupSummaries').findBy('name', this.get('name')); + return maybe(this.get('job.taskGroupSummaries')).findBy('name', this.get('name')); }), }); diff --git a/ui/app/serializers/job-summary.js b/ui/app/serializers/job-summary.js new file mode 100644 index 000000000..c3c8f67cd --- /dev/null +++ b/ui/app/serializers/job-summary.js @@ -0,0 +1,32 @@ +import { get } from '@ember/object'; +import ApplicationSerializer from './application'; + +export default ApplicationSerializer.extend({ + normalize(modelClass, hash) { + // Transform the map-based Summary object into an array-based + // TaskGroupSummary fragment list + hash.PlainJobId = hash.JobID; + hash.ID = JSON.stringify([hash.JobID, hash.Namespace || 'default']); + + hash.TaskGroupSummaries = Object.keys(get(hash, 'Summary') || {}).map(key => { + const allocStats = get(hash, `Summary.${key}`) || {}; + const summary = { Name: key }; + + Object.keys(allocStats).forEach( + allocKey => (summary[`${allocKey}Allocs`] = allocStats[allocKey]) + ); + + return summary; + }); + + // Lift the children stats out of the Children object + const childrenStats = get(hash, 'Children'); + if (childrenStats) { + Object.keys(childrenStats).forEach( + childrenKey => (hash[`${childrenKey}Children`] = childrenStats[childrenKey]) + ); + } + + return this._super(modelClass, hash); + }, +}); diff --git a/ui/app/serializers/job.js b/ui/app/serializers/job.js index df77e2f38..4ab4af882 100644 --- a/ui/app/serializers/job.js +++ b/ui/app/serializers/job.js @@ -34,27 +34,6 @@ export default ApplicationSerializer.extend({ hash.ParameterizedJob = true; } - // Transform the map-based JobSummary object into an array-based - // JobSummary fragment list - hash.TaskGroupSummaries = Object.keys(get(hash, 'JobSummary.Summary') || {}).map(key => { - const allocStats = get(hash, `JobSummary.Summary.${key}`) || {}; - const summary = { Name: key }; - - Object.keys(allocStats).forEach( - allocKey => (summary[`${allocKey}Allocs`] = allocStats[allocKey]) - ); - - return summary; - }); - - // Lift the children stats out of the JobSummary object - const childrenStats = get(hash, 'JobSummary.Children'); - if (childrenStats) { - Object.keys(childrenStats).forEach( - childrenKey => (hash[`${childrenKey}Children`] = childrenStats[childrenKey]) - ); - } - return this._super(typeHash, hash); }, @@ -68,6 +47,11 @@ export default ApplicationSerializer.extend({ .buildURL(modelName, hash.PlainId, hash, 'findRecord'); return assign(this._super(...arguments), { + summary: { + links: { + related: buildURL(`${jobURL}/summary`, { namespace: namespace }), + }, + }, allocations: { links: { related: buildURL(`${jobURL}/allocations`, { namespace: namespace }),