From 24eadd269c233f07eea40b6b9f8167d0be86fbab Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Wed, 10 Jun 2020 08:49:16 -0500 Subject: [PATCH 01/14] Add massaged results of class codemod MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manual interventions: • decorators on the same line for service and controller injections and most computed property macros • preserving import order when possible, both per-line and intra-line • moving new imports to the bottom • removal of classic decorator for trivial cases • conversion of init to constructor when appropriate --- ui/.eslintrc.js | 3 + ui/app/abilities/abstract.js | 26 +- ui/app/abilities/allocation.js | 12 +- ui/app/abilities/client.js | 12 +- ui/app/abilities/job.js | 12 +- ui/app/adapters/agent.js | 9 +- ui/app/adapters/application.js | 40 +- ui/app/adapters/deployment.js | 6 +- ui/app/adapters/job-summary.js | 8 +- ui/app/adapters/namespace.js | 8 +- ui/app/adapters/node.js | 16 +- ui/app/adapters/policy.js | 6 +- ui/app/adapters/token.js | 10 +- ui/app/adapters/watchable.js | 26 +- ui/app/app.js | 10 +- ui/app/components/allocation-row.js | 47 +- ui/app/components/allocation-stat.js | 44 +- ui/app/components/allocation-subnav.js | 19 +- ui/app/components/app-breadcrumbs.js | 14 +- ui/app/components/attributes-section.js | 8 +- ui/app/components/children-status-bar.js | 23 +- ui/app/components/client-node-row.js | 35 +- ui/app/components/copy-button.js | 19 +- ui/app/components/drain-popover.js | 63 +- ui/app/components/exec-terminal.js | 14 +- ui/app/components/exec/open-button.js | 26 +- ui/app/components/exec/task-contents.js | 8 +- ui/app/components/fs/breadcrumbs.js | 25 +- ui/app/components/fs/browser.js | 59 +- ui/app/components/fs/directory-entry.js | 19 +- ui/app/components/fs/file.js | 116 +- ui/app/components/fs/link.js | 14 +- ui/app/components/gauge-chart.js | 60 +- ui/app/components/global-header.js | 11 +- ui/app/components/gutter-menu.js | 19 +- ui/app/components/image-file.js | 30 +- ui/app/components/job-deployment.js | 14 +- .../job-deployment/deployment-metrics.js | 8 +- ui/app/components/job-deployments-stream.js | 28 +- .../components/job-diff-fields-and-objects.js | 8 +- ui/app/components/job-diff.js | 22 +- ui/app/components/job-editor.js | 72 +- ui/app/components/job-page/abstract.js | 38 +- ui/app/components/job-page/batch.js | 4 +- .../job-page/parameterized-child.js | 14 +- ui/app/components/job-page/parameterized.js | 4 +- ui/app/components/job-page/parts/children.js | 36 +- ui/app/components/job-page/parts/error.js | 14 +- .../job-page/parts/latest-deployment.js | 20 +- .../job-page/parts/placement-failures.js | 11 +- .../job-page/parts/recent-allocations.js | 31 +- ui/app/components/job-page/parts/summary.js | 18 +- .../components/job-page/parts/task-groups.js | 27 +- ui/app/components/job-page/parts/title.js | 26 +- ui/app/components/job-page/periodic-child.js | 11 +- ui/app/components/job-page/periodic.js | 40 +- ui/app/components/job-page/service.js | 4 +- ui/app/components/job-page/system.js | 4 +- ui/app/components/job-row.js | 20 +- ui/app/components/job-version.js | 32 +- ui/app/components/job-versions-stream.js | 25 +- ui/app/components/json-viewer.js | 16 +- ui/app/components/lifecycle-chart-row.js | 20 +- ui/app/components/lifecycle-chart.js | 29 +- ui/app/components/list-accordion.js | 25 +- .../list-accordion/accordion-body.js | 11 +- .../list-accordion/accordion-head.js | 26 +- ui/app/components/list-pagination.js | 39 +- .../components/list-pagination/list-pager.js | 8 +- ui/app/components/list-table.js | 19 +- ui/app/components/list-table/sort-by.js | 37 +- ui/app/components/list-table/table-body.js | 8 +- ui/app/components/list-table/table-head.js | 8 +- ui/app/components/multi-select-dropdown.js | 138 +- ui/app/components/page-layout.js | 12 +- ui/app/components/placement-failure.js | 12 +- ui/app/components/plugin-allocation-row.js | 14 +- ui/app/components/popover-menu.js | 56 +- ui/app/components/primary-metric.js | 68 +- ui/app/components/proxy-tag.js | 8 +- ui/app/components/region-switcher.js | 19 +- ui/app/components/reschedule-event-row.js | 24 +- ui/app/components/search-box.js | 39 +- ui/app/components/server-agent-row.js | 26 +- ui/app/components/stats-time-series.js | 37 +- ui/app/components/streaming-file.js | 52 +- ui/app/components/task-group-row.js | 19 +- ui/app/components/task-log.js | 98 +- ui/app/components/task-row.js | 52 +- ui/app/components/task-subnav.js | 20 +- ui/app/components/toggle.js | 22 +- ui/app/components/two-step-button.js | 79 +- .../allocations/allocation/fs-root.js | 2 +- .../controllers/allocations/allocation/fs.js | 27 +- .../allocations/allocation/task/fs-root.js | 2 +- .../allocations/allocation/task/fs.js | 27 +- .../allocations/allocation/task/index.js | 36 +- ui/app/controllers/application.js | 56 +- ui/app/controllers/clients.js | 6 +- ui/app/controllers/clients/index.js | 281 ++-- ui/app/controllers/csi/plugins.js | 6 +- ui/app/controllers/csi/plugins/index.js | 85 +- .../csi/plugins/plugin/allocations.js | 100 +- .../controllers/csi/plugins/plugin/index.js | 25 +- ui/app/controllers/csi/volumes.js | 12 +- ui/app/controllers/csi/volumes/index.js | 127 +- ui/app/controllers/csi/volumes/volume.js | 27 +- ui/app/controllers/jobs.js | 14 +- ui/app/controllers/jobs/index.js | 160 +-- ui/app/controllers/jobs/job/allocations.js | 51 +- ui/app/controllers/jobs/job/definition.js | 18 +- ui/app/controllers/jobs/job/deployments.js | 8 +- ui/app/controllers/jobs/job/evaluations.js | 25 +- ui/app/controllers/jobs/job/index.js | 41 +- ui/app/controllers/jobs/job/task-group.js | 53 +- ui/app/controllers/jobs/job/versions.js | 8 +- ui/app/controllers/jobs/run.js | 6 +- ui/app/controllers/servers.js | 26 +- ui/app/controllers/servers/index.js | 8 +- ui/app/controllers/servers/server.js | 24 +- ui/app/models/agent.js | 34 +- ui/app/models/allocation.js | 124 +- ui/app/models/consul-connect.js | 6 +- ui/app/models/deployment.js | 65 +- ui/app/models/drain-strategy.js | 16 +- ui/app/models/evaluation.js | 34 +- ui/app/models/host-volume.js | 12 +- ui/app/models/job-plan.js | 10 +- ui/app/models/job-summary.js | 39 +- ui/app/models/job-version.js | 14 +- ui/app/models/job.js | 217 +-- ui/app/models/lifecycle.js | 10 +- ui/app/models/namespace.js | 10 +- ui/app/models/network.js | 16 +- ui/app/models/node-attributes.js | 13 +- ui/app/models/node-driver.js | 32 +- ui/app/models/node-event.js | 16 +- ui/app/models/node.js | 120 +- ui/app/models/placement-failure.js | 28 +- ui/app/models/plugin.js | 36 +- ui/app/models/policy.js | 12 +- ui/app/models/reschedule-event.js | 18 +- ui/app/models/resources.js | 14 +- ui/app/models/service.js | 12 +- ui/app/models/sidecar-proxy-upstream.js | 8 +- ui/app/models/sidecar-proxy.js | 6 +- ui/app/models/sidecar-service.js | 6 +- ui/app/models/storage-controller.js | 28 +- ui/app/models/storage-node.js | 28 +- ui/app/models/task-event.js | 18 +- .../models/task-group-deployment-summary.js | 30 +- ui/app/models/task-group-summary.js | 29 +- ui/app/models/task-group.js | 51 +- ui/app/models/task-state.js | 50 +- ui/app/models/task.js | 29 +- ui/app/models/token.js | 20 +- ui/app/models/volume-definition.js | 18 +- ui/app/models/volume-mount.js | 25 +- ui/app/models/volume.js | 61 +- ui/app/router.js | 8 +- ui/app/routes/allocations/allocation.js | 17 +- .../routes/allocations/allocation/fs-root.js | 6 +- ui/app/routes/allocations/allocation/fs.js | 10 +- ui/app/routes/allocations/allocation/index.js | 10 +- ui/app/routes/allocations/allocation/task.js | 10 +- .../allocations/allocation/task/fs-root.js | 6 +- .../routes/allocations/allocation/task/fs.js | 14 +- .../allocations/allocation/task/logs.js | 8 +- ui/app/routes/clients/client.js | 28 +- ui/app/routes/clients/index.js | 10 +- ui/app/routes/csi/index.js | 6 +- ui/app/routes/csi/plugins/index.js | 10 +- ui/app/routes/csi/plugins/plugin.js | 16 +- .../routes/csi/plugins/plugin/allocations.js | 12 +- ui/app/routes/csi/plugins/plugin/index.js | 10 +- ui/app/routes/csi/volumes/index.js | 10 +- ui/app/routes/csi/volumes/volume.js | 22 +- ui/app/routes/exec/task-group/task.js | 12 +- ui/app/routes/index.js | 6 +- ui/app/routes/jobs/index.js | 10 +- ui/app/routes/jobs/job.js | 14 +- ui/app/routes/jobs/job/allocations.js | 12 +- ui/app/routes/jobs/job/definition.js | 8 +- ui/app/routes/jobs/job/deployments.js | 14 +- ui/app/routes/jobs/job/evaluations.js | 12 +- ui/app/routes/jobs/job/index.js | 23 +- ui/app/routes/jobs/job/task-group.js | 18 +- ui/app/routes/jobs/job/versions.js | 12 +- ui/app/routes/not-found.js | 6 +- ui/app/routes/servers/server.js | 2 +- ui/app/serializers/application.js | 20 +- ui/app/serializers/drain-strategy.js | 8 +- ui/app/serializers/evaluation.js | 10 +- ui/app/serializers/fragment.js | 2 +- ui/app/serializers/job-plan.js | 8 +- ui/app/serializers/job-summary.js | 8 +- ui/app/serializers/namespace.js | 6 +- ui/app/serializers/node-attributes.js | 8 +- ui/app/serializers/plugin.js | 8 +- ui/app/serializers/policy.js | 8 +- ui/app/serializers/reschedule-event.js | 8 +- .../task-group-deployment-summary.js | 8 +- ui/app/serializers/task-group.js | 8 +- ui/app/serializers/task-state.js | 8 +- ui/app/serializers/task.js | 8 +- ui/app/services/breadcrumbs.js | 13 +- ui/app/services/config.js | 12 +- ui/app/services/sockets.js | 6 +- ui/app/services/stats-trackers-registry.js | 19 +- ui/app/services/system.js | 152 +-- ui/app/services/token.js | 66 +- ui/app/services/user-settings.js | 6 +- ui/app/services/watch-list.js | 21 +- .../utils/classes/allocation-stats-tracker.js | 41 +- ui/app/utils/classes/log.js | 57 +- ui/app/utils/classes/node-stats-tracker.js | 32 +- ui/app/utils/classes/poll-logger.js | 17 +- ui/app/utils/classes/promise-array.js | 4 +- ui/app/utils/classes/promise-object.js | 4 +- ui/app/utils/classes/query-params.js | 10 +- ui/package.json | 2 + ui/yarn.lock | 1183 +++++++++++++++-- 222 files changed, 4332 insertions(+), 2826 deletions(-) diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js index 51f829062..5a0e65e68 100644 --- a/ui/.eslintrc.js +++ b/ui/.eslintrc.js @@ -15,6 +15,9 @@ module.exports = { parserOptions: { ecmaVersion: 2018, sourceType: 'module', + ecmaFeatures: { + legacyDecorators: true, + }, }, plugins: [ 'ember' diff --git a/ui/app/abilities/abstract.js b/ui/app/abilities/abstract.js index 6f45ae795..d8af277d4 100644 --- a/ui/app/abilities/abstract.js +++ b/ui/app/abilities/abstract.js @@ -2,19 +2,23 @@ import { Ability } from 'ember-can'; import { inject as service } from '@ember/service'; import { computed, get } from '@ember/object'; import { equal, not } from '@ember/object/computed'; +import classic from 'ember-classic-decorator'; -export default Ability.extend({ - system: service(), - token: service(), +@classic +export default class Abstract extends Ability { + @service system; + @service token; - bypassAuthorization: not('token.aclEnabled'), - selfTokenIsManagement: equal('token.selfToken.type', 'management'), + @not('token.aclEnabled') bypassAuthorization; + @equal('token.selfToken.type', 'management') selfTokenIsManagement; - activeNamespace: computed('system.activeNamespace.name', function() { + @computed('system.activeNamespace.name') + get activeNamespace() { return this.get('system.activeNamespace.name') || 'default'; - }), + } - rulesForActiveNamespace: computed('activeNamespace', 'token.selfTokenPolicies.[]', function() { + @computed('activeNamespace', 'token.selfTokenPolicies.[]') + get rulesForActiveNamespace() { let activeNamespace = this.activeNamespace; return (this.get('token.selfTokenPolicies') || []).toArray().reduce((rules, policy) => { @@ -28,7 +32,7 @@ export default Ability.extend({ return rules; }, []); - }), + } // Chooses the closest namespace as described at the bottom here: // https://www.nomadproject.io/guides/security/acl.html#namespace-rules @@ -67,5 +71,5 @@ export default Ability.extend({ } else if (namespaceNames.includes('default')) { return 'default'; } - }, -}); + } +} diff --git a/ui/app/abilities/allocation.js b/ui/app/abilities/allocation.js index e5ce8c325..0ac627049 100644 --- a/ui/app/abilities/allocation.js +++ b/ui/app/abilities/allocation.js @@ -2,13 +2,15 @@ import AbstractAbility from './abstract'; import { computed, get } from '@ember/object'; import { or } from '@ember/object/computed'; -export default AbstractAbility.extend({ - canExec: or('bypassAuthorization', 'selfTokenIsManagement', 'policiesSupportExec'), +export default class Allocation extends AbstractAbility { + @or('bypassAuthorization', 'selfTokenIsManagement', 'policiesSupportExec') + canExec; - policiesSupportExec: computed('rulesForActiveNamespace.@each.capabilities', function() { + @computed('rulesForActiveNamespace.@each.capabilities') + get policiesSupportExec() { return this.rulesForActiveNamespace.some(rules => { let capabilities = get(rules, 'Capabilities') || []; return capabilities.includes('alloc-exec'); }); - }), -}); + } +} diff --git a/ui/app/abilities/client.js b/ui/app/abilities/client.js index 58380081c..e8a94df34 100644 --- a/ui/app/abilities/client.js +++ b/ui/app/abilities/client.js @@ -2,12 +2,14 @@ import AbstractAbility from './abstract'; import { computed, get } from '@ember/object'; import { or } from '@ember/object/computed'; -export default AbstractAbility.extend({ +export default class Client extends AbstractAbility { // Map abilities to policy options (which are coarse for nodes) // instead of specific behaviors. - canWrite: or('bypassAuthorization', 'selfTokenIsManagement', 'policiesIncludeNodeWrite'), + @or('bypassAuthorization', 'selfTokenIsManagement', 'policiesIncludeNodeWrite') + canWrite; - policiesIncludeNodeWrite: computed('token.selfTokenPolicies.[]', function() { + @computed('token.selfTokenPolicies.[]') + get policiesIncludeNodeWrite() { // For each policy record, extract the Node policy const policies = (this.get('token.selfTokenPolicies') || []) .toArray() @@ -16,5 +18,5 @@ export default AbstractAbility.extend({ // Node write is allowed if any policy allows it return policies.some(policy => policy === 'write'); - }), -}); + } +} diff --git a/ui/app/abilities/job.js b/ui/app/abilities/job.js index 07b4edc73..1f8418c15 100644 --- a/ui/app/abilities/job.js +++ b/ui/app/abilities/job.js @@ -2,13 +2,15 @@ import AbstractAbility from './abstract'; import { computed, get } from '@ember/object'; import { or } from '@ember/object/computed'; -export default AbstractAbility.extend({ - canRun: or('bypassAuthorization', 'selfTokenIsManagement', 'policiesSupportRunning'), +export default class Job extends AbstractAbility { + @or('bypassAuthorization', 'selfTokenIsManagement', 'policiesSupportRunning') + canRun; - policiesSupportRunning: computed('rulesForActiveNamespace.@each.capabilities', function() { + @computed('rulesForActiveNamespace.@each.capabilities') + get policiesSupportRunning() { return this.rulesForActiveNamespace.some(rules => { let capabilities = get(rules, 'Capabilities') || []; return capabilities.includes('submit-job'); }); - }), -}); + } +} diff --git a/ui/app/adapters/agent.js b/ui/app/adapters/agent.js index 3e68a1d35..ab259e9ef 100644 --- a/ui/app/adapters/agent.js +++ b/ui/app/adapters/agent.js @@ -1,9 +1,10 @@ import ApplicationAdapter from './application'; -export default ApplicationAdapter.extend({ - pathForType: () => 'agent/members', +export default class Agent extends ApplicationAdapter { + pathForType = () => 'agent/members'; + urlForFindRecord() { const [, ...args] = arguments; return this.urlForFindAll(...args); - }, -}); + } +} diff --git a/ui/app/adapters/application.js b/ui/app/adapters/application.js index 7bbf51b02..3373fef90 100644 --- a/ui/app/adapters/application.js +++ b/ui/app/adapters/application.js @@ -4,20 +4,23 @@ import RESTAdapter from 'ember-data/adapters/rest'; import codesForError from '../utils/codes-for-error'; import removeRecord from '../utils/remove-record'; import { default as NoLeaderError, NO_LEADER } from '../utils/no-leader-error'; +import classic from 'ember-classic-decorator'; export const namespace = 'v1'; -export default RESTAdapter.extend({ +@classic +export default class Application extends RESTAdapter { // TODO: This can be removed once jquery-integration is turned off for // the entire app. - useFetch: true, + useFetch = true; - namespace, + namespace = namespace; - system: service(), - token: service(), + @service system; + @service token; - headers: computed('token.secret', function() { + @computed('token.secret') + get headers() { const token = this.get('token.secret'); if (token) { return { @@ -26,17 +29,17 @@ export default RESTAdapter.extend({ } return; - }), + } handleResponse(status, headers, payload) { if (status === 500 && payload === NO_LEADER) { return new NoLeaderError(); } - return this._super(...arguments); - }, + return super.handleResponse(...arguments); + } findAll() { - return this._super(...arguments).catch(error => { + return super.findAll(...arguments).catch(error => { const errorCodes = codesForError(error); const isNotImplemented = errorCodes.includes('501'); @@ -48,7 +51,7 @@ export default RESTAdapter.extend({ // Rethrow to be handled downstream throw error; }); - }, + } ajaxOptions(url, type, options = {}) { options.data || (options.data = {}); @@ -58,13 +61,13 @@ export default RESTAdapter.extend({ options.data.region = region; } } - return this._super(url, type, options); - }, + return super.ajaxOptions(url, type, options); + } // In order to remove stale records from the store, findHasMany has to unload // all records related to the request in question. findHasMany(store, snapshot, link, relationship) { - return this._super(...arguments).then(payload => { + return super.findHasMany(...arguments).then(payload => { const relationshipType = relationship.type; const inverse = snapshot.record.inverseFor(relationship.key); if (inverse) { @@ -77,7 +80,7 @@ export default RESTAdapter.extend({ } return payload; }); - }, + } // Single record requests deviate from REST practice by using // the singular form of the resource name. @@ -87,9 +90,10 @@ export default RESTAdapter.extend({ // // This is the original implementation of _buildURL // without the pluralization of modelName - urlForFindRecord: urlForRecord, - urlForUpdateRecord: urlForRecord, -}); + urlForFindRecord = urlForRecord; + + urlForUpdateRecord = urlForRecord; +} function urlForRecord(id, modelName) { let path; diff --git a/ui/app/adapters/deployment.js b/ui/app/adapters/deployment.js index 6dbc20dbd..b45f22e56 100644 --- a/ui/app/adapters/deployment.js +++ b/ui/app/adapters/deployment.js @@ -1,6 +1,6 @@ import Watchable from './watchable'; -export default Watchable.extend({ +export default class Deployment extends Watchable { promote(deployment) { const id = deployment.get('id'); const url = urlForAction(this.urlForFindRecord(id, 'deployment'), '/promote'); @@ -10,8 +10,8 @@ export default Watchable.extend({ All: true, }, }); - }, -}); + } +} // The deployment action API endpoints all end with the ID // /deployment/:action/:deployment_id instead of /deployment/:deployment_id/:action diff --git a/ui/app/adapters/job-summary.js b/ui/app/adapters/job-summary.js index e21a54068..92a35bb5d 100644 --- a/ui/app/adapters/job-summary.js +++ b/ui/app/adapters/job-summary.js @@ -1,12 +1,12 @@ import Watchable from './watchable'; -export default Watchable.extend({ +export default class JobSummary extends Watchable { urlForFindRecord(id, type, hash) { const [name, namespace] = JSON.parse(id); - let url = this._super(name, 'job', hash) + '/summary'; + let url = super.urlForFindRecord(name, 'job', hash) + '/summary'; if (namespace && namespace !== 'default') { url += `?namespace=${namespace}`; } return url; - }, -}); + } +} diff --git a/ui/app/adapters/namespace.js b/ui/app/adapters/namespace.js index 9b4b04df8..89a004578 100644 --- a/ui/app/adapters/namespace.js +++ b/ui/app/adapters/namespace.js @@ -1,13 +1,13 @@ import ApplicationAdapter from './application'; import codesForError from '../utils/codes-for-error'; -export default ApplicationAdapter.extend({ +export default class Namespace extends ApplicationAdapter { findRecord(store, modelClass, id) { - return this._super(...arguments).catch(error => { + return super.findRecord(...arguments).catch(error => { const errorCodes = codesForError(error); if (errorCodes.includes('501')) { return { Name: id }; } }); - }, -}); + } +} diff --git a/ui/app/adapters/node.js b/ui/app/adapters/node.js index d541d70ad..cde6ef79f 100644 --- a/ui/app/adapters/node.js +++ b/ui/app/adapters/node.js @@ -1,14 +1,14 @@ import Watchable from './watchable'; import addToPath from 'nomad-ui/utils/add-to-path'; -export default Watchable.extend({ +export default class Node extends Watchable { setEligible(node) { return this.setEligibility(node, true); - }, + } setIneligible(node) { return this.setEligibility(node, false); - }, + } setEligibility(node, isEligible) { const url = addToPath(this.urlForFindRecord(node.id, 'node'), '/eligibility'); @@ -18,7 +18,7 @@ export default Watchable.extend({ Eligibility: isEligible ? 'eligible' : 'ineligible', }, }); - }, + } // Force: -1s deadline // No Deadline: 0 deadline @@ -36,7 +36,7 @@ export default Watchable.extend({ ), }, }); - }, + } forceDrain(node, drainSpec) { return this.drain( @@ -45,7 +45,7 @@ export default Watchable.extend({ Deadline: -1, }) ); - }, + } cancelDrain(node) { const url = addToPath(this.urlForFindRecord(node.id, 'node'), '/drain'); @@ -55,5 +55,5 @@ export default Watchable.extend({ DrainSpec: null, }, }); - }, -}); + } +} diff --git a/ui/app/adapters/policy.js b/ui/app/adapters/policy.js index 66f6c0b30..e21e301e7 100644 --- a/ui/app/adapters/policy.js +++ b/ui/app/adapters/policy.js @@ -1,5 +1,5 @@ import { default as ApplicationAdapter, namespace } from './application'; -export default ApplicationAdapter.extend({ - namespace: namespace + '/acl', -}); +export default class Policy extends ApplicationAdapter { + namespace = namespace + '/acl'; +} diff --git a/ui/app/adapters/token.js b/ui/app/adapters/token.js index 7db388248..b0a149dc0 100644 --- a/ui/app/adapters/token.js +++ b/ui/app/adapters/token.js @@ -1,10 +1,10 @@ import { inject as service } from '@ember/service'; import { default as ApplicationAdapter, namespace } from './application'; -export default ApplicationAdapter.extend({ - store: service(), +export default class Token extends ApplicationAdapter { + @service store; - namespace: namespace + '/acl', + namespace = namespace + '/acl'; findSelf() { return this.ajax(`${this.buildURL()}/token/self`, 'GET').then(token => { @@ -15,5 +15,5 @@ export default ApplicationAdapter.extend({ return store.peekRecord('token', store.normalize('token', token).data.id); }); - }, -}); + } +} diff --git a/ui/app/adapters/watchable.js b/ui/app/adapters/watchable.js index 983c87b8b..feb07b316 100644 --- a/ui/app/adapters/watchable.js +++ b/ui/app/adapters/watchable.js @@ -6,9 +6,9 @@ import queryString from 'query-string'; import ApplicationAdapter from './application'; import removeRecord from '../utils/remove-record'; -export default ApplicationAdapter.extend({ - watchList: service(), - store: service(), +export default class Watchable extends ApplicationAdapter { + @service watchList; + @service store; // Overriding ajax is not advised, but this is a minimal modification // that sets off a series of events that results in query params being @@ -19,7 +19,7 @@ export default ApplicationAdapter.extend({ // to ajaxOptions or overriding ajax completely. ajax(url, type, options) { const hasParams = hasNonBlockingQueryParams(options); - if (!hasParams || type !== 'GET') return this._super(url, type, options); + if (!hasParams || type !== 'GET') return super.ajax(url, type, options); const params = { ...options.data }; delete params.index; @@ -29,8 +29,8 @@ export default ApplicationAdapter.extend({ // at this point since everything else is added to the URL in advance. options.data = options.data.index ? { index: options.data.index } : {}; - return this._super(`${url}?${queryString.stringify(params)}`, type, options); - }, + return super.ajax(`${url}?${queryString.stringify(params)}`, type, options); + } findAll(store, type, sinceToken, snapshotRecordArray, additionalParams = {}) { const params = assign(this.buildQuery(), additionalParams); @@ -45,7 +45,7 @@ export default ApplicationAdapter.extend({ signal, data: params, }); - }, + } findRecord(store, type, id, snapshot, additionalParams = {}) { let [url, params] = this.buildURL(type.modelName, id, snapshot, 'findRecord').split('?'); @@ -65,7 +65,7 @@ export default ApplicationAdapter.extend({ } throw error; }); - }, + } query(store, type, query, snapshotRecordArray, options, additionalParams = {}) { const url = this.buildURL(type.modelName, null, null, 'query', query); @@ -107,7 +107,7 @@ export default ApplicationAdapter.extend({ return payload; }); - }, + } reloadRelationship(model, relationshipName, options = { watch: false, abortController: null }) { const { watch, abortController } = options; @@ -156,7 +156,7 @@ export default ApplicationAdapter.extend({ } ); } - }, + } handleResponse(status, headers, payload, requestData) { // Some browsers lowercase all headers. Others keep them @@ -166,9 +166,9 @@ export default ApplicationAdapter.extend({ this.watchList.setIndexFor(requestData.url, newIndex); } - return this._super(...arguments); - }, -}); + return super.handleResponse(...arguments); + } +} function hasNonBlockingQueryParams(options) { if (!options || !options.data) return false; diff --git a/ui/app/app.js b/ui/app/app.js index 7d6bae3ce..5e9a038ba 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -5,11 +5,11 @@ import config from './config/environment'; let App; -App = Application.extend({ - modulePrefix: config.modulePrefix, - podModulePrefix: config.podModulePrefix, - Resolver, -}); +App = class AppApplication extends Application { + modulePrefix = config.modulePrefix; + podModulePrefix = config.podModulePrefix; + Resolver = Resolver; +}; loadInitializers(App, config.modulePrefix); diff --git a/ui/app/components/allocation-row.js b/ui/app/components/allocation-row.js index c2e0d46a5..c5d829b26 100644 --- a/ui/app/components/allocation-row.js +++ b/ui/app/components/allocation-row.js @@ -8,46 +8,48 @@ import { run } from '@ember/runloop'; import { task, timeout } from 'ember-concurrency'; import { lazyClick } from '../helpers/lazy-click'; import AllocationStatsTracker from 'nomad-ui/utils/classes/allocation-stats-tracker'; +import classic from 'ember-classic-decorator'; +import { classNames, tagName } from '@ember-decorators/component'; -export default Component.extend({ - store: service(), - token: service(), +@classic +@tagName('tr') +@classNames('allocation-row', 'is-interactive') +export default class AllocationRow extends Component { + @service store; + @service token; - tagName: 'tr', - - classNames: ['allocation-row', 'is-interactive'], - - allocation: null, + allocation = null; // Used to determine whether the row should mention the node or the job - context: null, + context = null; // Internal state - statsError: false, + statsError = false; - enablePolling: overridable(() => !Ember.testing), + @overridable(() => !Ember.testing) enablePolling; - stats: computed('allocation', 'allocation.isRunning', function() { + @computed('allocation', 'allocation.isRunning') + get stats() { if (!this.get('allocation.isRunning')) return; return AllocationStatsTracker.create({ fetch: url => this.token.authorizedRequest(url), allocation: this.allocation, }); - }), + } - cpu: alias('stats.cpu.lastObject'), - memory: alias('stats.memory.lastObject'), + @alias('stats.cpu.lastObject') cpu; + @alias('stats.memory.lastObject') memory; - onClick() {}, + onClick() {} click(event) { lazyClick([this.onClick, event]); - }, + } didReceiveAttrs() { this.updateStatsTracker(); - }, + } updateStatsTracker() { const allocation = this.allocation; @@ -57,9 +59,9 @@ export default Component.extend({ } else { this.fetchStats.cancelAll(); } - }, + } - fetchStats: task(function*() { + @(task(function*() { do { if (this.stats) { try { @@ -72,8 +74,9 @@ export default Component.extend({ yield timeout(500); } while (this.enablePolling); - }).drop(), -}); + }).drop()) + fetchStats; +} async function qualifyAllocation() { const allocation = this.allocation; diff --git a/ui/app/components/allocation-stat.js b/ui/app/components/allocation-stat.js index b1d702a13..b740910e0 100644 --- a/ui/app/components/allocation-stat.js +++ b/ui/app/components/allocation-stat.js @@ -2,41 +2,47 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { alias } from '@ember/object/computed'; import { formatBytes } from 'nomad-ui/helpers/format-bytes'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', +@classic +@tagName('') +export default class AllocationStat extends Component { + allocation = null; + statsTracker = null; + isLoading = false; + error = null; + metric = 'memory'; // Either memory or cpu - allocation: null, - statsTracker: null, - isLoading: false, - error: null, - metric: 'memory', // Either memory or cpu - - statClass: computed('metric', function() { + @computed('metric') + get statClass() { return this.metric === 'cpu' ? 'is-info' : 'is-danger'; - }), + } - cpu: alias('statsTracker.cpu.lastObject'), - memory: alias('statsTracker.memory.lastObject'), + @alias('statsTracker.cpu.lastObject') cpu; + @alias('statsTracker.memory.lastObject') memory; - stat: computed('metric', 'cpu', 'memory', function() { + @computed('metric', 'cpu', 'memory') + get stat() { const { metric } = this; if (metric === 'cpu' || metric === 'memory') { return this[this.metric]; } return; - }), + } - formattedStat: computed('metric', 'stat.used', function() { + @computed('metric', 'stat.used') + get formattedStat() { if (!this.stat) return; if (this.metric === 'memory') return formatBytes([this.stat.used]); return this.stat.used; - }), + } - formattedReserved: computed('metric', 'statsTracker.{reservedMemory,reservedCPU}', function() { + @computed('metric', 'statsTracker.{reservedMemory,reservedCPU}') + get formattedReserved() { if (this.metric === 'memory') return `${this.statsTracker.reservedMemory} MiB`; if (this.metric === 'cpu') return `${this.statsTracker.reservedCPU} MHz`; return; - }), -}); + } +} diff --git a/ui/app/components/allocation-subnav.js b/ui/app/components/allocation-subnav.js index 133d18a27..594440223 100644 --- a/ui/app/components/allocation-subnav.js +++ b/ui/app/components/allocation-subnav.js @@ -1,14 +1,19 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; import { equal, or } from '@ember/object/computed'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - router: service(), +@classic +@tagName('') +export default class AllocationSubnav extends Component { + @service router; - tagName: '', + @equal('router.currentRouteName', 'allocations.allocation.fs') + fsIsActive; - fsIsActive: equal('router.currentRouteName', 'allocations.allocation.fs'), - fsRootIsActive: equal('router.currentRouteName', 'allocations.allocation.fs-root'), + @equal('router.currentRouteName', 'allocations.allocation.fs-root') + fsRootIsActive; - filesLinkActive: or('fsIsActive', 'fsRootIsActive'), -}); + @or('fsIsActive', 'fsRootIsActive') filesLinkActive; +} diff --git a/ui/app/components/app-breadcrumbs.js b/ui/app/components/app-breadcrumbs.js index 28a08a85d..1537febbd 100644 --- a/ui/app/components/app-breadcrumbs.js +++ b/ui/app/components/app-breadcrumbs.js @@ -1,11 +1,13 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; import { reads } from '@ember/object/computed'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - breadcrumbsService: service('breadcrumbs'), +@classic +@tagName('') +export default class AppBreadcrumbs extends Component { + @service('breadcrumbs') breadcrumbsService; - tagName: '', - - breadcrumbs: reads('breadcrumbsService.breadcrumbs'), -}); + @reads('breadcrumbsService.breadcrumbs') breadcrumbs; +} diff --git a/ui/app/components/attributes-section.js b/ui/app/components/attributes-section.js index 479865264..6e1c763e5 100644 --- a/ui/app/components/attributes-section.js +++ b/ui/app/components/attributes-section.js @@ -1,5 +1,7 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', -}); +@classic +@tagName('') +export default class AttributesSection extends Component {} diff --git a/ui/app/components/children-status-bar.js b/ui/app/components/children-status-bar.js index 63c62e481..5ea7d6eb7 100644 --- a/ui/app/components/children-status-bar.js +++ b/ui/app/components/children-status-bar.js @@ -1,27 +1,26 @@ import { computed } from '@ember/object'; import DistributionBar from './distribution-bar'; +import classic from 'ember-classic-decorator'; -export default DistributionBar.extend({ - layoutName: 'components/distribution-bar', +@classic +export default class ChildrenStatusBar extends DistributionBar { + layoutName = 'components/distribution-bar'; - job: null, + job = null; - 'data-test-children-status-bar': true, + 'data-test-children-status-bar' = true; - data: computed('job.{pendingChildren,runningChildren,deadChildren}', function() { + @computed('job.{pendingChildren,runningChildren,deadChildren}') + get data() { if (!this.job) { return []; } - const children = this.job.getProperties( - 'pendingChildren', - 'runningChildren', - 'deadChildren' - ); + const children = this.job.getProperties('pendingChildren', 'runningChildren', 'deadChildren'); return [ { label: 'Pending', value: children.pendingChildren, className: 'queued' }, { label: 'Running', value: children.runningChildren, className: 'running' }, { label: 'Dead', value: children.deadChildren, className: 'complete' }, ]; - }), -}); + } +} diff --git a/ui/app/components/client-node-row.js b/ui/app/components/client-node-row.js index 4eaec00f8..2127b5930 100644 --- a/ui/app/components/client-node-row.js +++ b/ui/app/components/client-node-row.js @@ -4,20 +4,22 @@ import { lazyClick } from '../helpers/lazy-click'; import { watchRelationship } from 'nomad-ui/utils/properties/watch'; import WithVisibilityDetection from 'nomad-ui/mixins/with-component-visibility-detection'; import { computed } from '@ember/object'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend(WithVisibilityDetection, { - store: service(), +@classic +@tagName('tr') +@classNames('client-node-row', 'is-interactive') +export default class ClientNodeRow extends Component.extend(WithVisibilityDetection) { + @service store; - tagName: 'tr', - classNames: ['client-node-row', 'is-interactive'], + node = null; - node: null, - - onClick() {}, + onClick() {} click(event) { lazyClick([this.onClick, event]); - }, + } didReceiveAttrs() { // Reload the node in order to get detail information @@ -27,7 +29,7 @@ export default Component.extend(WithVisibilityDetection, { this.watch.perform(node, 100); }); } - }, + } visibilityHandler() { if (document.hidden) { @@ -38,16 +40,17 @@ export default Component.extend(WithVisibilityDetection, { this.watch.perform(node, 100); } } - }, + } willDestroy() { this.watch.cancelAll(); - this._super(...arguments); - }, + super.willDestroy(...arguments); + } - watch: watchRelationship('allocations'), + @watchRelationship('allocations') watch; - compositeStatusClass: computed('node.compositeStatus', function() { + @computed('node.compositeStatus') + get compositeStatusClass() { let compositeStatus = this.get('node.compositeStatus'); if (compositeStatus === 'draining') { @@ -59,5 +62,5 @@ export default Component.extend(WithVisibilityDetection, { } else { return ''; } - }), -}); + } +} diff --git a/ui/app/components/copy-button.js b/ui/app/components/copy-button.js index d8a891d85..107786d73 100644 --- a/ui/app/components/copy-button.js +++ b/ui/app/components/copy-button.js @@ -1,16 +1,19 @@ import Component from '@ember/component'; import { task, timeout } from 'ember-concurrency'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - classNames: ['copy-button'], +@classic +@classNames('copy-button') +export default class CopyButton extends Component { + clipboardText = null; + state = null; - clipboardText: null, - state: null, - - indicateSuccess: task(function*() { + @(task(function*() { this.set('state', 'success'); yield timeout(2000); this.set('state', null); - }).restartable(), -}); + }).restartable()) + indicateSuccess; +} diff --git a/ui/app/components/drain-popover.js b/ui/app/components/drain-popover.js index f964a8081..6690e9d72 100644 --- a/ui/app/components/drain-popover.js +++ b/ui/app/components/drain-popover.js @@ -4,30 +4,34 @@ import { equal } from '@ember/object/computed'; import { computed as overridable } from 'ember-overridable-computed'; import { task } from 'ember-concurrency'; import Duration from 'duration-js'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', +@classic +@tagName('') +export default class DrainPopover extends Component { + client = null; + isDisabled = false; - client: null, - isDisabled: false, + onError() {} + onDrain() {} - onError() {}, - onDrain() {}, + parseError = ''; - parseError: '', + deadlineEnabled = false; + forceDrain = false; + drainSystemJobs = true; - deadlineEnabled: false, - forceDrain: false, - drainSystemJobs: true, - - selectedDurationQuickOption: overridable(function() { + @overridable(function() { return this.durationQuickOptions[0]; - }), + }) + selectedDurationQuickOption; - durationIsCustom: equal('selectedDurationQuickOption.value', 'custom'), - customDuration: '', + @equal('selectedDurationQuickOption.value', 'custom') durationIsCustom; + customDuration = ''; - durationQuickOptions: computed(function() { + @computed + get durationQuickOptions() { return [ { label: '1 Hour', value: '1h' }, { label: '4 Hours', value: '4h' }, @@ -36,21 +40,21 @@ export default Component.extend({ { label: '1 Day', value: '1d' }, { label: 'Custom', value: 'custom' }, ]; - }), + } - deadline: computed( + @computed( 'deadlineEnabled', 'durationIsCustom', 'customDuration', - 'selectedDurationQuickOption.value', - function() { - if (!this.deadlineEnabled) return 0; - if (this.durationIsCustom) return this.customDuration; - return this.selectedDurationQuickOption.value; - } - ), + 'selectedDurationQuickOption.value' + ) + get deadline() { + if (!this.deadlineEnabled) return 0; + if (this.durationIsCustom) return this.customDuration; + return this.selectedDurationQuickOption.value; + } - drain: task(function*(close) { + @task(function*(close) { if (!this.client) return; const isUpdating = this.client.isDraining; @@ -79,9 +83,10 @@ export default Component.extend({ } catch (err) { this.onError(err); } - }), + }) + drain; preventDefault(e) { e.preventDefault(); - }, -}); + } +} diff --git a/ui/app/components/exec-terminal.js b/ui/app/components/exec-terminal.js index 255b8f588..e396677a7 100644 --- a/ui/app/components/exec-terminal.js +++ b/ui/app/components/exec-terminal.js @@ -1,10 +1,12 @@ import Component from '@ember/component'; import { FitAddon } from 'xterm-addon-fit'; import WindowResizable from '../mixins/window-resizable'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend(WindowResizable, { - classNames: ['terminal-container'], - +@classic +@classNames('terminal-container') +export default class ExecTerminal extends Component.extend(WindowResizable) { didInsertElement() { let fitAddon = new FitAddon(); this.fitAddon = fitAddon; @@ -13,12 +15,12 @@ export default Component.extend(WindowResizable, { this.terminal.open(this.element.querySelector('.terminal')); fitAddon.fit(); - }, + } windowResizeHandler(e) { this.fitAddon.fit(); if (this.terminal.resized) { this.terminal.resized(e); } - }, -}); + } +} diff --git a/ui/app/components/exec/open-button.js b/ui/app/components/exec/open-button.js index 0525cc022..84b552318 100644 --- a/ui/app/components/exec/open-button.js +++ b/ui/app/components/exec/open-button.js @@ -1,25 +1,27 @@ import Component from '@ember/component'; +import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import generateExecUrl from 'nomad-ui/utils/generate-exec-url'; import openExecUrl from 'nomad-ui/utils/open-exec-url'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', +@classic +@tagName('') +export default class OpenButton extends Component { + @service router; - router: service(), - - actions: { - open() { - openExecUrl(this.generateUrl()); - }, - }, + @action + open() { + openExecUrl(this.generateUrl()); + } generateUrl() { return generateExecUrl(this.router, { job: this.job, taskGroup: this.taskGroup, task: this.task, - allocation: this.task + allocation: this.task, }); - }, -}); + } +} diff --git a/ui/app/components/exec/task-contents.js b/ui/app/components/exec/task-contents.js index 479865264..e4ccbb5e3 100644 --- a/ui/app/components/exec/task-contents.js +++ b/ui/app/components/exec/task-contents.js @@ -1,5 +1,7 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', -}); +@classic +@tagName('') +export default class TaskContents extends Component {} diff --git a/ui/app/components/fs/breadcrumbs.js b/ui/app/components/fs/breadcrumbs.js index 784de27ca..819e822b6 100644 --- a/ui/app/components/fs/breadcrumbs.js +++ b/ui/app/components/fs/breadcrumbs.js @@ -1,18 +1,21 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { isEmpty } from '@ember/utils'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'nav', - classNames: ['breadcrumb'], +@classic +@tagName('nav') +@classNames('breadcrumb') +export default class Breadcrumbs extends Component { + 'data-test-fs-breadcrumbs' = true; - 'data-test-fs-breadcrumbs': true, + allocation = null; + taskState = null; + path = null; - allocation: null, - taskState: null, - path: null, - - breadcrumbs: computed('path', function() { + @computed('path') + get breadcrumbs() { const breadcrumbs = this.path .split('/') .reject(isEmpty) @@ -39,5 +42,5 @@ export default Component.extend({ } return breadcrumbs; - }), -}); + } +} diff --git a/ui/app/components/fs/browser.js b/ui/app/components/fs/browser.js index bf6b30731..d18a15777 100644 --- a/ui/app/components/fs/browser.js +++ b/ui/app/components/fs/browser.js @@ -1,58 +1,59 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { filterBy } from '@ember/object/computed'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', +@classic +@tagName('') +export default class Browser extends Component { + model = null; - model: null, - - allocation: computed('model', function() { + @computed('model') + get allocation() { if (this.model.allocation) { return this.model.allocation; } else { return this.model; } - }), + } - taskState: computed('model', function() { + @computed('model') + get taskState() { if (this.model.allocation) { return this.model; } return; - }), + } - type: computed('taskState', function() { + @computed('taskState') + get type() { if (this.taskState) { return 'task'; } else { return 'allocation'; } - }), + } - directories: filterBy('directoryEntries', 'IsDir'), - files: filterBy('directoryEntries', 'IsDir', false), + @filterBy('directoryEntries', 'IsDir') directories; + @filterBy('directoryEntries', 'IsDir', false) files; - sortedDirectoryEntries: computed( - 'directoryEntries.[]', - 'sortProperty', - 'sortDescending', - function() { - const sortProperty = this.sortProperty; + @computed('directoryEntries.[]', 'sortProperty', 'sortDescending') + get sortedDirectoryEntries() { + const sortProperty = this.sortProperty; - const directorySortProperty = sortProperty === 'Size' ? 'Name' : sortProperty; + const directorySortProperty = sortProperty === 'Size' ? 'Name' : sortProperty; - const sortedDirectories = this.directories.sortBy(directorySortProperty); - const sortedFiles = this.files.sortBy(sortProperty); + const sortedDirectories = this.directories.sortBy(directorySortProperty); + const sortedFiles = this.files.sortBy(sortProperty); - const sortedDirectoryEntries = sortedDirectories.concat(sortedFiles); + const sortedDirectoryEntries = sortedDirectories.concat(sortedFiles); - if (this.sortDescending) { - return sortedDirectoryEntries.reverse(); - } else { - return sortedDirectoryEntries; - } + if (this.sortDescending) { + return sortedDirectoryEntries.reverse(); + } else { + return sortedDirectoryEntries; } - ), -}); + } +} diff --git a/ui/app/components/fs/directory-entry.js b/ui/app/components/fs/directory-entry.js index 3b703dd90..69fe41c3a 100644 --- a/ui/app/components/fs/directory-entry.js +++ b/ui/app/components/fs/directory-entry.js @@ -1,14 +1,17 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { isEmpty } from '@ember/utils'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', +@classic +@tagName('') +export default class DirectoryEntry extends Component { + allocation = null; + taskState = null; - allocation: null, - taskState: null, - - pathToEntry: computed('path', 'entry.Name', function() { + @computed('path', 'entry.Name') + get pathToEntry() { const pathWithNoLeadingSlash = this.get('path').replace(/^\//, ''); const name = encodeURIComponent(this.get('entry.Name')); @@ -17,5 +20,5 @@ export default Component.extend({ } else { return `${pathWithNoLeadingSlash}/${name}`; } - }), -}); + } +} diff --git a/ui/app/components/fs/file.js b/ui/app/components/fs/file.js index c8e6a06ec..4b282bb1c 100644 --- a/ui/app/components/fs/file.js +++ b/ui/app/components/fs/file.js @@ -1,36 +1,38 @@ import { inject as service } from '@ember/service'; import Component from '@ember/component'; -import { computed } from '@ember/object'; -import { gt } from '@ember/object/computed'; -import { equal } from '@ember/object/computed'; +import { action, computed } from '@ember/object'; +import { equal, gt } from '@ember/object/computed'; import RSVP from 'rsvp'; import Log from 'nomad-ui/utils/classes/log'; import timeout from 'nomad-ui/utils/timeout'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - token: service(), +@classic +@classNames('boxed-section', 'task-log') +export default class File extends Component { + @service token; - classNames: ['boxed-section', 'task-log'], + 'data-test-file-viewer' = true; - 'data-test-file-viewer': true, - - allocation: null, - taskState: null, - file: null, - stat: null, // { Name, IsDir, Size, FileMode, ModTime, ContentType } + allocation = null; + taskState = null; + file = null; + stat = null; // { Name, IsDir, Size, FileMode, ModTime, ContentType } // When true, request logs from the server agent - useServer: false, + useServer = false; // When true, logs cannot be fetched from either the client or the server - noConnection: false, + noConnection = false; - clientTimeout: 1000, - serverTimeout: 5000, + clientTimeout = 1000; + serverTimeout = 5000; - mode: 'head', + mode = 'head'; - fileComponent: computed('stat.ContentType', function() { + @computed('stat.ContentType') + get fileComponent() { const contentType = this.stat.ContentType || ''; if (contentType.startsWith('image/')) { @@ -40,21 +42,23 @@ export default Component.extend({ } else { return 'unknown'; } - }), + } - isLarge: gt('stat.Size', 50000), + @gt('stat.Size', 50000) isLarge; - fileTypeIsUnknown: equal('fileComponent', 'unknown'), - isStreamable: equal('fileComponent', 'stream'), - isStreaming: false, + @equal('fileComponent', 'unknown') fileTypeIsUnknown; + @equal('fileComponent', 'stream') isStreamable; + isStreaming = false; - catUrl: computed('allocation.id', 'taskState.name', 'file', function() { + @computed('allocation.id', 'taskState.name', 'file') + get catUrl() { const taskUrlPrefix = this.taskState ? `${this.taskState.name}/` : ''; const encodedPath = encodeURIComponent(`${taskUrlPrefix}${this.file}`); return `/v1/client/fs/cat/${this.allocation.id}?path=${encodedPath}`; - }), + } - fetchMode: computed('isLarge', 'mode', function() { + @computed('isLarge', 'mode') + get fetchMode() { if (this.mode === 'streaming') { return 'stream'; } @@ -66,15 +70,17 @@ export default Component.extend({ } return; - }), + } - fileUrl: computed('allocation.{id,node.httpAddr}', 'fetchMode', 'useServer', function() { + @computed('allocation.{id,node.httpAddr}', 'fetchMode', 'useServer') + get fileUrl() { const address = this.get('allocation.node.httpAddr'); const url = `/v1/client/fs/${this.fetchMode}/${this.allocation.id}`; return this.useServer ? url : `//${address}${url}`; - }), + } - fileParams: computed('taskState.name', 'file', 'mode', function() { + @computed('taskState.name', 'file', 'mode') + get fileParams() { // The Log class handles encoding query params const taskUrlPrefix = this.taskState ? `${this.taskState.name}/` : ''; const path = `${taskUrlPrefix}${this.file}`; @@ -89,9 +95,10 @@ export default Component.extend({ default: return { path }; } - }), + } - logger: computed('fileUrl', 'fileParams', 'mode', function() { + @computed('fileUrl', 'fileParams', 'mode') + get logger() { // The cat and readat APIs are in plainText while the stream API is always encoded. const plainText = this.mode === 'head' || this.mode === 'tail'; @@ -115,7 +122,7 @@ export default Component.extend({ params: this.fileParams, url: this.fileUrl, }); - }), + } nextErrorState(error) { if (this.useServer) { @@ -124,23 +131,28 @@ export default Component.extend({ this.send('failoverToServer'); } throw error; - }, + } - actions: { - toggleStream() { - this.set('mode', 'streaming'); - this.toggleProperty('isStreaming'); - }, - gotoHead() { - this.set('mode', 'head'); - this.set('isStreaming', false); - }, - gotoTail() { - this.set('mode', 'tail'); - this.set('isStreaming', false); - }, - failoverToServer() { - this.set('useServer', true); - }, - }, -}); + @action + toggleStream() { + this.set('mode', 'streaming'); + this.toggleProperty('isStreaming'); + } + + @action + gotoHead() { + this.set('mode', 'head'); + this.set('isStreaming', false); + } + + @action + gotoTail() { + this.set('mode', 'tail'); + this.set('isStreaming', false); + } + + @action + failoverToServer() { + this.set('useServer', true); + } +} diff --git a/ui/app/components/fs/link.js b/ui/app/components/fs/link.js index c6a4aee26..59aeb1452 100644 --- a/ui/app/components/fs/link.js +++ b/ui/app/components/fs/link.js @@ -1,8 +1,10 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', - - allocation: null, - taskState: null, -}); +@classic +@tagName('') +export default class Link extends Component { + allocation = null; + taskState = null; +} diff --git a/ui/app/components/gauge-chart.js b/ui/app/components/gauge-chart.js index 3df1f5e4d..9625dbff2 100644 --- a/ui/app/components/gauge-chart.js +++ b/ui/app/components/gauge-chart.js @@ -5,19 +5,22 @@ import { guidFor } from '@ember/object/internals'; import { run } from '@ember/runloop'; import d3Shape from 'd3-shape'; import WindowResizable from 'nomad-ui/mixins/window-resizable'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend(WindowResizable, { - classNames: ['chart', 'gauge-chart'], +@classic +@classNames('chart', 'gauge-chart') +export default class GaugeChart extends Component.extend(WindowResizable) { + value = null; + complement = null; + total = null; + chartClass = 'is-info'; - value: null, - complement: null, - total: null, - chartClass: 'is-info', + width = 0; + height = 0; - width: 0, - height: 0, - - percent: computed('value', 'complement', 'total', function() { + @computed('value', 'complement', 'total') + get percent() { assert( 'Provide complement OR total to GaugeChart, not both.', this.complement != null || this.total != null @@ -28,23 +31,27 @@ export default Component.extend(WindowResizable, { } return this.value / this.total; - }), + } - fillId: computed(function() { + @computed + get fillId() { return `gauge-chart-fill-${guidFor(this)}`; - }), + } - maskId: computed(function() { + @computed + get maskId() { return `gauge-chart-mask-${guidFor(this)}`; - }), + } - radius: computed('width', function() { + @computed('width') + get radius() { return this.width / 2; - }), + } - weight: 4, + weight = 4; - backgroundArc: computed('radius', 'weight', function() { + @computed('radius', 'weight') + get backgroundArc() { const { radius, weight } = this; const arc = d3Shape .arc() @@ -54,9 +61,10 @@ export default Component.extend(WindowResizable, { .startAngle(-Math.PI / 2) .endAngle(Math.PI / 2); return arc(); - }), + } - valueArc: computed('radius', 'weight', 'percent', function() { + @computed('radius', 'weight', 'percent') + get valueArc() { const { radius, weight, percent } = this; const arc = d3Shape @@ -67,18 +75,18 @@ export default Component.extend(WindowResizable, { .startAngle(-Math.PI / 2) .endAngle(-Math.PI / 2 + Math.PI * percent); return arc(); - }), + } didInsertElement() { this.updateDimensions(); - }, + } updateDimensions() { const width = this.element.querySelector('svg').clientWidth; this.setProperties({ width, height: width / 2 }); - }, + } windowResizeHandler() { run.once(this, this.updateDimensions); - }, -}); + } +} diff --git a/ui/app/components/global-header.js b/ui/app/components/global-header.js index c90e6457d..a7fbc9d88 100644 --- a/ui/app/components/global-header.js +++ b/ui/app/components/global-header.js @@ -1,7 +1,8 @@ import Component from '@ember/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - 'data-test-global-header': true, - - onHamburgerClick() {}, -}); +@classic +export default class GlobalHeader extends Component { + 'data-test-global-header' = true; + onHamburgerClick() {} +} diff --git a/ui/app/components/gutter-menu.js b/ui/app/components/gutter-menu.js index 46c02d137..469ac039b 100644 --- a/ui/app/components/gutter-menu.js +++ b/ui/app/components/gutter-menu.js @@ -1,12 +1,15 @@ import { inject as service } from '@ember/service'; import Component from '@ember/component'; import { computed } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - system: service(), - router: service(), +@classic +export default class GutterMenu extends Component { + @service system; + @service router; - sortedNamespaces: computed('system.namespaces.@each.name', function() { + @computed('system.namespaces.@each.name') + get sortedNamespaces() { const namespaces = this.get('system.namespaces').toArray() || []; return namespaces.sort((a, b) => { @@ -30,9 +33,9 @@ export default Component.extend({ return 0; }); - }), + } - onHamburgerClick() {}, + onHamburgerClick() {} gotoJobsForNamespace(namespace) { if (!namespace || !namespace.get('id')) return; @@ -46,5 +49,5 @@ export default Component.extend({ this.router.transitionTo(destination, { queryParams: { namespace: namespace.get('id') }, }); - }, -}); + } +} diff --git a/ui/app/components/image-file.js b/ui/app/components/image-file.js index fe28201ba..2e359a171 100644 --- a/ui/app/components/image-file.js +++ b/ui/app/components/image-file.js @@ -1,23 +1,27 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'figure', - classNames: 'image-file', - 'data-test-image-file': true, +@classic +@tagName('figure') +@classNames('image-file') +export default class ImageFile extends Component { + 'data-test-image-file' = true; - src: null, - alt: null, - size: null, + src = null; + alt = null; + size = null; // Set by updateImageMeta - width: 0, - height: 0, + width = 0; + height = 0; - fileName: computed('src', function() { + @computed('src') + get fileName() { if (!this.src) return; return this.src.includes('/') ? this.src.match(/^.*\/(.*)$/)[1] : this.src; - }), + } updateImageMeta(event) { const img = event.target; @@ -25,5 +29,5 @@ export default Component.extend({ width: img.naturalWidth, height: img.naturalHeight, }); - }, -}); + } +} diff --git a/ui/app/components/job-deployment.js b/ui/app/components/job-deployment.js index feb13f17d..f9b31ecf7 100644 --- a/ui/app/components/job-deployment.js +++ b/ui/app/components/job-deployment.js @@ -1,8 +1,10 @@ import Component from '@ember/component'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - classNames: ['job-deployment', 'boxed-section'], - - deployment: null, - isOpen: false, -}); +@classic +@classNames('job-deployment', 'boxed-section') +export default class JobDeployment extends Component { + deployment = null; + isOpen = false; +} diff --git a/ui/app/components/job-deployment/deployment-metrics.js b/ui/app/components/job-deployment/deployment-metrics.js index 479865264..213414561 100644 --- a/ui/app/components/job-deployment/deployment-metrics.js +++ b/ui/app/components/job-deployment/deployment-metrics.js @@ -1,5 +1,7 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', -}); +@classic +@tagName('') +export default class DeploymentMetrics extends Component {} diff --git a/ui/app/components/job-deployments-stream.js b/ui/app/components/job-deployments-stream.js index 7a5e3d5d5..49b534d3e 100644 --- a/ui/app/components/job-deployments-stream.js +++ b/ui/app/components/job-deployments-stream.js @@ -2,20 +2,22 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { computed as overridable } from 'ember-overridable-computed'; import moment from 'moment'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'ol', - classNames: ['timeline'], +@classic +@tagName('ol') +@classNames('timeline') +export default class JobDeploymentsStream extends Component { + @overridable(() => []) deployments; - deployments: overridable(() => []), + @computed('deployments.@each.versionSubmitTime') + get sortedDeployments() { + return this.deployments.sortBy('versionSubmitTime').reverse(); + } - sortedDeployments: computed('deployments.@each.versionSubmitTime', function() { - return this.deployments - .sortBy('versionSubmitTime') - .reverse(); - }), - - annotatedDeployments: computed('sortedDeployments.@each.version', function() { + @computed('sortedDeployments.@each.version') + get annotatedDeployments() { const deployments = this.sortedDeployments; return deployments.map((deployment, index) => { const meta = {}; @@ -39,5 +41,5 @@ export default Component.extend({ return { deployment, meta }; }); - }), -}); + } +} diff --git a/ui/app/components/job-diff-fields-and-objects.js b/ui/app/components/job-diff-fields-and-objects.js index 479865264..20812480b 100644 --- a/ui/app/components/job-diff-fields-and-objects.js +++ b/ui/app/components/job-diff-fields-and-objects.js @@ -1,5 +1,7 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', -}); +@classic +@tagName('') +export default class JobDiffFieldsAndObjects extends Component {} diff --git a/ui/app/components/job-diff.js b/ui/app/components/job-diff.js index 79371f333..2f7a6921c 100644 --- a/ui/app/components/job-diff.js +++ b/ui/app/components/job-diff.js @@ -1,15 +1,17 @@ import { equal } from '@ember/object/computed'; import Component from '@ember/component'; +import { classNames, classNameBindings } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - classNames: ['job-diff'], - classNameBindings: ['isEdited:is-edited', 'isAdded:is-added', 'isDeleted:is-deleted'], +@classic +@classNames('job-diff') +@classNameBindings('isEdited:is-edited', 'isAdded:is-added', 'isDeleted:is-deleted') +export default class JobDiff extends Component { + diff = null; - diff: null, + verbose = true; - verbose: true, - - isEdited: equal('diff.Type', 'Edited'), - isAdded: equal('diff.Type', 'Added'), - isDeleted: equal('diff.Type', 'Deleted'), -}); + @equal('diff.Type', 'Edited') isEdited; + @equal('diff.Type', 'Added') isAdded; + @equal('diff.Type', 'Deleted') isDeleted; +} diff --git a/ui/app/components/job-editor.js b/ui/app/components/job-editor.js index fa4bf4e8f..27c0998b9 100644 --- a/ui/app/components/job-editor.js +++ b/ui/app/components/job-editor.js @@ -5,44 +5,48 @@ import { computed } from '@ember/object'; import { task } from 'ember-concurrency'; import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error'; import localStorageProperty from 'nomad-ui/utils/properties/local-storage'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - store: service(), - config: service(), +@classic +export default class JobEditor extends Component { + @service store; + @service config; - 'data-test-job-editor': true, + 'data-test-job-editor' = true; - job: null, - onSubmit() {}, - context: computed({ - get() { - return this._context; - }, - set(key, value) { - const allowedValues = ['new', 'edit']; + job = null; + onSubmit() {} - assert(`context must be one of: ${allowedValues.join(', ')}`, allowedValues.includes(value)); + @computed + get context() { + return this._context; + } - this.set('_context', value); - return value; - }, - }), + set context(value) { + const allowedValues = ['new', 'edit']; - _context: null, - parseError: null, - planError: null, - runError: null, + assert(`context must be one of: ${allowedValues.join(', ')}`, allowedValues.includes(value)); - planOutput: null, + this.set('_context', value); + return value; + } - showPlanMessage: localStorageProperty('nomadMessageJobPlan', true), - showEditorMessage: localStorageProperty('nomadMessageJobEditor', true), + _context = null; + parseError = null; + planError = null; + runError = null; - stage: computed('planOutput', function() { + planOutput = null; + + @localStorageProperty('nomadMessageJobPlan', true) showPlanMessage; + @localStorageProperty('nomadMessageJobEditor', true) showEditorMessage; + + @computed('planOutput') + get stage() { return this.planOutput ? 'plan' : 'editor'; - }), + } - plan: task(function*() { + @(task(function*() { this.reset(); try { @@ -62,9 +66,10 @@ export default Component.extend({ this.set('planError', error); this.scrollToError(); } - }).drop(), + }).drop()) + plan; - submit: task(function*() { + @task(function*() { try { if (this.context === 'new') { yield this.job.run(); @@ -85,18 +90,19 @@ export default Component.extend({ this.set('planOutput', null); this.scrollToError(); } - }), + }) + submit; reset() { this.set('planOutput', null); this.set('planError', null); this.set('parseError', null); this.set('runError', null); - }, + } scrollToError() { if (!this.get('config.isTest')) { window.scrollTo(0, 0); } - }, -}); + } +} diff --git a/ui/app/components/job-page/abstract.js b/ui/app/components/job-page/abstract.js index 931c5c6db..29beaa6aa 100644 --- a/ui/app/components/job-page/abstract.js +++ b/ui/app/components/job-page/abstract.js @@ -1,28 +1,32 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - system: service(), +@classic +export default class Abstract extends Component { + @service system; - job: null, + job = null; // Provide a value that is bound to a query param - sortProperty: null, - sortDescending: null, + sortProperty = null; + sortDescending = null; // Provide actions that require routing - gotoTaskGroup() {}, - gotoJob() {}, + gotoTaskGroup() {} + gotoJob() {} // Set to a { title, description } to surface an error - errorMessage: null, + errorMessage = null; - actions: { - clearErrorMessage() { - this.set('errorMessage', null); - }, - handleError(errorObject) { - this.set('errorMessage', errorObject); - }, - }, -}); + @action + clearErrorMessage() { + this.set('errorMessage', null); + } + + @action + handleError(errorObject) { + this.set('errorMessage', errorObject); + } +} diff --git a/ui/app/components/job-page/batch.js b/ui/app/components/job-page/batch.js index 559b3c8b8..3aa31786c 100644 --- a/ui/app/components/job-page/batch.js +++ b/ui/app/components/job-page/batch.js @@ -1,3 +1,5 @@ import AbstractJobPage from './abstract'; +import classic from 'ember-classic-decorator'; -export default AbstractJobPage.extend(); +@classic +export default class Batch extends AbstractJobPage {} diff --git a/ui/app/components/job-page/parameterized-child.js b/ui/app/components/job-page/parameterized-child.js index c7b167bef..3f941067c 100644 --- a/ui/app/components/job-page/parameterized-child.js +++ b/ui/app/components/job-page/parameterized-child.js @@ -1,10 +1,14 @@ import { computed } from '@ember/object'; import { alias } from '@ember/object/computed'; import PeriodicChildJobPage from './periodic-child'; +import classic from 'ember-classic-decorator'; -export default PeriodicChildJobPage.extend({ - payload: alias('job.decodedPayload'), - payloadJSON: computed('payload', function() { +@classic +export default class ParameterizedChild extends PeriodicChildJobPage { + @alias('job.decodedPayload') payload; + + @computed('payload') + get payloadJSON() { let json; try { json = JSON.parse(this.payload); @@ -12,5 +16,5 @@ export default PeriodicChildJobPage.extend({ // Swallow error and fall back to plain text rendering } return json; - }), -}); + } +} diff --git a/ui/app/components/job-page/parameterized.js b/ui/app/components/job-page/parameterized.js index 559b3c8b8..cf69268ae 100644 --- a/ui/app/components/job-page/parameterized.js +++ b/ui/app/components/job-page/parameterized.js @@ -1,3 +1,5 @@ import AbstractJobPage from './abstract'; +import classic from 'ember-classic-decorator'; -export default AbstractJobPage.extend(); +@classic +export default class Parameterized extends AbstractJobPage {} diff --git a/ui/app/components/job-page/parts/children.js b/ui/app/components/job-page/parts/children.js index 30772cd0a..f3b4b865a 100644 --- a/ui/app/components/job-page/parts/children.js +++ b/ui/app/components/job-page/parts/children.js @@ -2,30 +2,34 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { alias } from '@ember/object/computed'; import Sortable from 'nomad-ui/mixins/sortable'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend(Sortable, { - job: null, - - classNames: ['boxed-section'], +@classic +@classNames('boxed-section') +export default class Children extends Component.extend(Sortable) { + job = null; // Provide a value that is bound to a query param - sortProperty: null, - sortDescending: null, - currentPage: null, + sortProperty = null; + sortDescending = null; + currentPage = null; // Provide an action with access to the router - gotoJob() {}, + gotoJob() {} - pageSize: 10, + pageSize = 10; - taskGroups: computed('job.taskGroups.[]', function() { + @computed('job.taskGroups.[]') + get taskGroups() { return this.get('job.taskGroups') || []; - }), + } - children: computed('job.children.[]', function() { + @computed('job.children.[]') + get children() { return this.get('job.children') || []; - }), + } - listToSort: alias('children'), - sortedChildren: alias('listSorted'), -}); + @alias('children') listToSort; + @alias('listSorted') sortedChildren; +} diff --git a/ui/app/components/job-page/parts/error.js b/ui/app/components/job-page/parts/error.js index 241bb1662..a9259cfaf 100644 --- a/ui/app/components/job-page/parts/error.js +++ b/ui/app/components/job-page/parts/error.js @@ -1,8 +1,10 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', - - errorMessage: null, - onDismiss() {}, -}); +@classic +@tagName('') +export default class Error extends Component { + errorMessage = null; + onDismiss() {} +} diff --git a/ui/app/components/job-page/parts/latest-deployment.js b/ui/app/components/job-page/parts/latest-deployment.js index dacfd7497..dc78a235e 100644 --- a/ui/app/components/job-page/parts/latest-deployment.js +++ b/ui/app/components/job-page/parts/latest-deployment.js @@ -2,16 +2,19 @@ import Component from '@ember/component'; import { task } from 'ember-concurrency'; import { ForbiddenError } from '@ember-data/adapter/error'; import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - job: null, - tagName: '', +@classic +@tagName('') +export default class LatestDeployment extends Component { + job = null; - handleError() {}, + handleError() {} - isShowingDeploymentDetails: false, + isShowingDeploymentDetails = false; - promote: task(function*() { + @task(function*() { try { yield this.get('job.latestDeployment.content').promote(); } catch (err) { @@ -26,5 +29,6 @@ export default Component.extend({ description: message, }); } - }), -}); + }) + promote; +} diff --git a/ui/app/components/job-page/parts/placement-failures.js b/ui/app/components/job-page/parts/placement-failures.js index 7df4236d8..12a4f9186 100644 --- a/ui/app/components/job-page/parts/placement-failures.js +++ b/ui/app/components/job-page/parts/placement-failures.js @@ -1,6 +1,9 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - job: null, - tagName: '', -}); +@classic +@tagName('') +export default class PlacementFailures extends Component { + job = null; +} diff --git a/ui/app/components/job-page/parts/recent-allocations.js b/ui/app/components/job-page/parts/recent-allocations.js index 005c5a4ba..507a0fc0f 100644 --- a/ui/app/components/job-page/parts/recent-allocations.js +++ b/ui/app/components/job-page/parts/recent-allocations.js @@ -1,16 +1,20 @@ import Component from '@ember/component'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import { inject as service } from '@ember/service'; import PromiseArray from 'nomad-ui/utils/classes/promise-array'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - classNames: ['boxed-section'], +@classic +@classNames('boxed-section') +export default class RecentAllocations extends Component { + @service router; - router: service(), + sortProperty = 'modifyIndex'; + sortDescending = true; - sortProperty: 'modifyIndex', - sortDescending: true, - sortedAllocations: computed('job.allocations.@each.modifyIndex', function() { + @computed('job.allocations.@each.modifyIndex') + get sortedAllocations() { return PromiseArray.create({ promise: this.get('job.allocations').then(allocations => allocations @@ -19,11 +23,10 @@ export default Component.extend({ .slice(0, 5) ), }); - }), + } - actions: { - gotoAllocation(allocation) { - this.router.transitionTo('allocations.allocation', allocation.id); - }, - }, -}); + @action + gotoAllocation(allocation) { + this.router.transitionTo('allocations.allocation', allocation.id); + } +} diff --git a/ui/app/components/job-page/parts/summary.js b/ui/app/components/job-page/parts/summary.js index ba6211659..87ca0a4c3 100644 --- a/ui/app/components/job-page/parts/summary.js +++ b/ui/app/components/job-page/parts/summary.js @@ -1,17 +1,21 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - job: null, - classNames: ['boxed-section'], +@classic +@classNames('boxed-section') +export default class Summary extends Component { + job = null; - isExpanded: computed(function() { + @computed + get isExpanded() { const storageValue = window.localStorage.nomadExpandJobSummary; return storageValue != null ? JSON.parse(storageValue) : true; - }), + } persist(item, isOpen) { window.localStorage.nomadExpandJobSummary = isOpen; this.notifyPropertyChange('isExpanded'); - }, -}); + } +} diff --git a/ui/app/components/job-page/parts/task-groups.js b/ui/app/components/job-page/parts/task-groups.js index f5ce33757..129bb5f5a 100644 --- a/ui/app/components/job-page/parts/task-groups.js +++ b/ui/app/components/job-page/parts/task-groups.js @@ -2,23 +2,26 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { alias } from '@ember/object/computed'; import Sortable from 'nomad-ui/mixins/sortable'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend(Sortable, { - job: null, - - classNames: ['boxed-section'], +@classic +@classNames('boxed-section') +export default class TaskGroups extends Component.extend(Sortable) { + job = null; // Provide a value that is bound to a query param - sortProperty: null, - sortDescending: null, + sortProperty = null; + sortDescending = null; // Provide an action with access to the router - gotoTaskGroup() {}, + gotoTaskGroup() {} - taskGroups: computed('job.taskGroups.[]', function() { + @computed('job.taskGroups.[]') + get taskGroups() { return this.get('job.taskGroups') || []; - }), + } - listToSort: alias('taskGroups'), - sortedTaskGroups: alias('listSorted'), -}); + @alias('taskGroups') listToSort; + @alias('listSorted') sortedTaskGroups; +} diff --git a/ui/app/components/job-page/parts/title.js b/ui/app/components/job-page/parts/title.js index b13702d64..97dba1927 100644 --- a/ui/app/components/job-page/parts/title.js +++ b/ui/app/components/job-page/parts/title.js @@ -2,16 +2,18 @@ import Component from '@ember/component'; import { task } from 'ember-concurrency'; import { ForbiddenError } from '@ember-data/adapter/error'; import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', +@classic +@tagName('') +export default class Title extends Component { + job = null; + title = null; - job: null, - title: null, + handleError() {} - handleError() {}, - - stopJob: task(function*() { + @task(function*() { try { const job = this.job; yield job.stop(); @@ -23,9 +25,10 @@ export default Component.extend({ description: 'Your ACL token does not grant permission to stop jobs.', }); } - }), + }) + stopJob; - startJob: task(function*() { + @task(function*() { const job = this.job; const definition = yield job.fetchRawDefinition(); @@ -49,5 +52,6 @@ export default Component.extend({ description: message, }); } - }), -}); + }) + startJob; +} diff --git a/ui/app/components/job-page/periodic-child.js b/ui/app/components/job-page/periodic-child.js index 4eda0ed4d..dfe42225d 100644 --- a/ui/app/components/job-page/periodic-child.js +++ b/ui/app/components/job-page/periodic-child.js @@ -1,8 +1,11 @@ import AbstractJobPage from './abstract'; import { computed } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default AbstractJobPage.extend({ - breadcrumbs: computed('job.{name,id}', 'job.parent.{name,id}', function() { +@classic +export default class PeriodicChild extends AbstractJobPage { + @computed('job.{name,id}', 'job.parent.{name,id}') + get breadcrumbs() { const job = this.job; const parent = this.get('job.parent'); @@ -17,5 +20,5 @@ export default AbstractJobPage.extend({ args: ['jobs.job', job], }, ]; - }), -}); + } +} diff --git a/ui/app/components/job-page/periodic.js b/ui/app/components/job-page/periodic.js index c108fc2ab..f36818581 100644 --- a/ui/app/components/job-page/periodic.js +++ b/ui/app/components/job-page/periodic.js @@ -1,24 +1,26 @@ import AbstractJobPage from './abstract'; import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default AbstractJobPage.extend({ - store: service(), +@classic +export default class Periodic extends AbstractJobPage { + @service store; - errorMessage: null, + errorMessage = null; - actions: { - forceLaunch() { - this.job - .forcePeriodic() - .catch(() => { - this.set('errorMessage', { - title: 'Could Not Force Launch', - description: 'Your ACL token does not grant permission to submit jobs.', - }); - }); - }, - clearErrorMessage() { - this.set('errorMessage', null); - }, - }, -}); + @action + forceLaunch() { + this.job.forcePeriodic().catch(() => { + this.set('errorMessage', { + title: 'Could Not Force Launch', + description: 'Your ACL token does not grant permission to submit jobs.', + }); + }); + } + + @action + clearErrorMessage() { + this.set('errorMessage', null); + } +} diff --git a/ui/app/components/job-page/service.js b/ui/app/components/job-page/service.js index 559b3c8b8..0cb58e90c 100644 --- a/ui/app/components/job-page/service.js +++ b/ui/app/components/job-page/service.js @@ -1,3 +1,5 @@ import AbstractJobPage from './abstract'; +import classic from 'ember-classic-decorator'; -export default AbstractJobPage.extend(); +@classic +export default class Service extends AbstractJobPage {} diff --git a/ui/app/components/job-page/system.js b/ui/app/components/job-page/system.js index 559b3c8b8..bf2c04442 100644 --- a/ui/app/components/job-page/system.js +++ b/ui/app/components/job-page/system.js @@ -1,3 +1,5 @@ import AbstractJobPage from './abstract'; +import classic from 'ember-classic-decorator'; -export default AbstractJobPage.extend(); +@classic +export default class System extends AbstractJobPage {} diff --git a/ui/app/components/job-row.js b/ui/app/components/job-row.js index 4d55fd71a..954332b09 100644 --- a/ui/app/components/job-row.js +++ b/ui/app/components/job-row.js @@ -1,18 +1,20 @@ import { inject as service } from '@ember/service'; import Component from '@ember/component'; import { lazyClick } from '../helpers/lazy-click'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - store: service(), +@classic +@tagName('tr') +@classNames('job-row', 'is-interactive') +export default class JobRow extends Component { + @service store; - tagName: 'tr', - classNames: ['job-row', 'is-interactive'], + job = null; - job: null, - - onClick() {}, + onClick() {} click(event) { lazyClick([this.onClick, event]); - }, -}); + } +} diff --git a/ui/app/components/job-version.js b/ui/app/components/job-version.js index 978111a93..254996488 100644 --- a/ui/app/components/job-version.js +++ b/ui/app/components/job-version.js @@ -1,18 +1,21 @@ import Component from '@ember/component'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; const changeTypes = ['Added', 'Deleted', 'Edited']; -export default Component.extend({ - classNames: ['job-version', 'boxed-section'], - - version: null, - isOpen: false, +@classic +@classNames('job-version', 'boxed-section') +export default class JobVersion extends Component { + version = null; + isOpen = false; // Passes through to the job-diff component - verbose: true, + verbose = true; - changeCount: computed('version.diff', function() { + @computed('version.diff') + get changeCount() { const diff = this.get('version.diff'); const taskGroups = diff.TaskGroups || []; @@ -25,14 +28,13 @@ export default Component.extend({ taskGroups.reduce(arrayOfFieldChanges, 0) + (taskGroups.mapBy('Tasks') || []).reduce(flatten, []).reduce(arrayOfFieldChanges, 0) ); - }), + } - actions: { - toggleDiff() { - this.toggleProperty('isOpen'); - }, - }, -}); + @action + toggleDiff() { + this.toggleProperty('isOpen'); + } +} const flatten = (accumulator, array) => accumulator.concat(array); const countChanges = (total, field) => (changeTypes.includes(field.Type) ? total + 1 : total); diff --git a/ui/app/components/job-versions-stream.js b/ui/app/components/job-versions-stream.js index 3fc938ea0..5497565e5 100644 --- a/ui/app/components/job-versions-stream.js +++ b/ui/app/components/job-versions-stream.js @@ -2,20 +2,21 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { computed as overridable } from 'ember-overridable-computed'; import moment from 'moment'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'ol', - classNames: ['timeline'], - - versions: overridable(() => []), +@classic +@tagName('ol') +@classNames('timeline') +export default class JobVersionsStream extends Component { + @overridable(() => []) versions; // Passes through to the job-diff component - verbose: true, + verbose = true; - annotatedVersions: computed('versions.[]', function() { - const versions = this.versions - .sortBy('submitTime') - .reverse(); + @computed('versions.[]') + get annotatedVersions() { + const versions = this.versions.sortBy('submitTime').reverse(); return versions.map((version, index) => { const meta = {}; @@ -32,5 +33,5 @@ export default Component.extend({ return { version, meta }; }); - }), -}); + } +} diff --git a/ui/app/components/json-viewer.js b/ui/app/components/json-viewer.js index 63185cfc8..bcb803099 100644 --- a/ui/app/components/json-viewer.js +++ b/ui/app/components/json-viewer.js @@ -1,11 +1,15 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - classNames: ['json-viewer'], +@classic +@classNames('json-viewer') +export default class JsonViewer extends Component { + json = null; - json: null, - jsonStr: computed('json', function() { + @computed('json') + get jsonStr() { return JSON.stringify(this.json, null, 2); - }), -}); + } +} diff --git a/ui/app/components/lifecycle-chart-row.js b/ui/app/components/lifecycle-chart-row.js index 8ef057556..f63a50eba 100644 --- a/ui/app/components/lifecycle-chart-row.js +++ b/ui/app/components/lifecycle-chart-row.js @@ -1,22 +1,26 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', - - activeClass: computed('taskState.state', function() { +@classic +@tagName('') +export default class LifecycleChartRow extends Component { + @computed('taskState.state') + get activeClass() { if (this.taskState && this.taskState.state === 'running') { return 'is-active'; } return; - }), + } - finishedClass: computed('taskState.finishedAt', function() { + @computed('taskState.finishedAt') + get finishedClass() { if (this.taskState && this.taskState.finishedAt) { return 'is-finished'; } return; - }), -}); + } +} diff --git a/ui/app/components/lifecycle-chart.js b/ui/app/components/lifecycle-chart.js index f15b281d9..d2e1735ea 100644 --- a/ui/app/components/lifecycle-chart.js +++ b/ui/app/components/lifecycle-chart.js @@ -1,14 +1,17 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { sort } from '@ember/object/computed'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', +@classic +@tagName('') +export default class LifecycleChart extends Component { + tasks = null; + taskStates = null; - tasks: null, - taskStates: null, - - lifecyclePhases: computed('tasks.@each.lifecycle', 'taskStates.@each.state', function() { + @computed('tasks.@each.lifecycle', 'taskStates.@each.state') + get lifecyclePhases() { const tasksOrStates = this.taskStates || this.tasks; const lifecycles = { prestarts: [], @@ -38,16 +41,18 @@ export default Component.extend({ } return phases; - }), + } - sortedLifecycleTaskStates: sort('taskStates', function(a, b) { + @sort('taskStates', function(a, b) { return getTaskSortPrefix(a.task).localeCompare(getTaskSortPrefix(b.task)); - }), + }) + sortedLifecycleTaskStates; - sortedLifecycleTasks: sort('tasks', function(a, b) { + @sort('tasks', function(a, b) { return getTaskSortPrefix(a).localeCompare(getTaskSortPrefix(b)); - }), -}); + }) + sortedLifecycleTasks; +} const lifecycleNameSortPrefix = { prestart: 0, diff --git a/ui/app/components/list-accordion.js b/ui/app/components/list-accordion.js index 105288f1b..373c86f8b 100644 --- a/ui/app/components/list-accordion.js +++ b/ui/app/components/list-accordion.js @@ -1,17 +1,20 @@ import Component from '@ember/component'; import { computed, get } from '@ember/object'; import { computed as overridable } from 'ember-overridable-computed'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - classNames: ['accordion'], +@classic +@classNames('accordion') +export default class ListAccordion extends Component { + key = 'id'; + @overridable(() => []) source; - key: 'id', - source: overridable(() => []), + onToggle /* item, isOpen */() {} + startExpanded = false; - onToggle(/* item, isOpen */) {}, - startExpanded: false, - - decoratedSource: computed('source.[]', function() { + @computed('source.[]') + get decoratedSource() { const stateCache = this.stateCache; const key = this.key; const deepKey = `item.${key}`; @@ -28,9 +31,9 @@ export default Component.extend({ // eslint-disable-next-line ember/no-side-effects this.set('stateCache', decoratedSource); return decoratedSource; - }), + } // When source updates come in, the state cache is used to preserve // open/close state. - stateCache: overridable(() => []), -}); + @overridable(() => []) stateCache; +} diff --git a/ui/app/components/list-accordion/accordion-body.js b/ui/app/components/list-accordion/accordion-body.js index 32397fce6..492ee8de5 100644 --- a/ui/app/components/list-accordion/accordion-body.js +++ b/ui/app/components/list-accordion/accordion-body.js @@ -1,6 +1,9 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', - isOpen: false, -}); +@classic +@tagName('') +export default class AccordionBody extends Component { + isOpen = false; +} diff --git a/ui/app/components/list-accordion/accordion-head.js b/ui/app/components/list-accordion/accordion-head.js index 9cff6e92b..320092634 100644 --- a/ui/app/components/list-accordion/accordion-head.js +++ b/ui/app/components/list-accordion/accordion-head.js @@ -1,16 +1,18 @@ import Component from '@ember/component'; +import { classNames, classNameBindings } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - classNames: ['accordion-head'], - classNameBindings: ['isOpen::is-light', 'isExpandable::is-inactive'], +@classic +@classNames('accordion-head') +@classNameBindings('isOpen::is-light', 'isExpandable::is-inactive') +export default class AccordionHead extends Component { + 'data-test-accordion-head' = true; - 'data-test-accordion-head': true, + buttonLabel = 'toggle'; + isOpen = false; + isExpandable = true; + item = null; - buttonLabel: 'toggle', - isOpen: false, - isExpandable: true, - item: null, - - onClose() {}, - onOpen() {}, -}); + onClose() {} + onOpen() {} +} diff --git a/ui/app/components/list-pagination.js b/ui/app/components/list-pagination.js index 322a7db27..729d70684 100644 --- a/ui/app/components/list-pagination.js +++ b/ui/app/components/list-pagination.js @@ -1,26 +1,32 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { computed as overridable } from 'ember-overridable-computed'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - source: overridable(() => []), - size: 25, - page: 1, - spread: 2, +@classic +export default class ListPagination extends Component { + @overridable(() => []) source; + size = 25; + page = 1; + spread = 2; - startsAt: computed('size', 'page', function() { + @computed('size', 'page') + get startsAt() { return (this.page - 1) * this.size + 1; - }), + } - endsAt: computed('source.[]', 'size', 'page', function() { + @computed('source.[]', 'size', 'page') + get endsAt() { return Math.min(this.page * this.size, this.get('source.length')); - }), + } - lastPage: computed('source.[]', 'size', function() { + @computed('source.[]', 'size') + get lastPage() { return Math.ceil(this.get('source.length') / this.size); - }), + } - pageLinks: computed('source.[]', 'page', 'spread', function() { + @computed('source.[]', 'page', 'spread') + get pageLinks() { const { spread, page, lastPage } = this; // When there is only one page, don't bother with page links @@ -36,11 +42,12 @@ export default Component.extend({ .map((_, index) => ({ pageNumber: lowerBound + index, })); - }), + } - list: computed('source.[]', 'page', 'size', function() { + @computed('source.[]', 'page', 'size') + get list() { const size = this.size; const start = (this.page - 1) * size; return this.source.slice(start, start + size); - }), -}); + } +} diff --git a/ui/app/components/list-pagination/list-pager.js b/ui/app/components/list-pagination/list-pager.js index 479865264..64dae05b7 100644 --- a/ui/app/components/list-pagination/list-pager.js +++ b/ui/app/components/list-pagination/list-pager.js @@ -1,5 +1,7 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', -}); +@classic +@tagName('') +export default class ListPager extends Component {} diff --git a/ui/app/components/list-table.js b/ui/app/components/list-table.js index 439f1d6a4..0c06f542e 100644 --- a/ui/app/components/list-table.js +++ b/ui/app/components/list-table.js @@ -1,17 +1,20 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { computed as overridable } from 'ember-overridable-computed'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'table', - classNames: ['table'], - - source: overridable(() => []), +@classic +@tagName('table') +@classNames('table') +export default class ListTable extends Component { + @overridable(() => []) source; // Plan for a future with metadata (e.g., isSelected) - decoratedSource: computed('source.[]', function() { + @computed('source.[]') + get decoratedSource() { return this.source.map(row => ({ model: row, })); - }), -}); + } +} diff --git a/ui/app/components/list-table/sort-by.js b/ui/app/components/list-table/sort-by.js index 2186eb2e7..bdc16efa6 100644 --- a/ui/app/components/list-table/sort-by.js +++ b/ui/app/components/list-table/sort-by.js @@ -1,25 +1,32 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; +import { + classNames, + attributeBindings, + classNameBindings, + tagName, +} from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'th', - - attributeBindings: ['title'], - +@classic +@tagName('th') +@attributeBindings('title') +@classNames('is-selectable') +@classNameBindings('isActive:is-active', 'sortDescending:desc:asc') +export default class SortBy extends Component { // The prop that the table is currently sorted by - currentProp: '', + currentProp = ''; // The prop this sorter controls - prop: '', + prop = ''; - classNames: ['is-selectable'], - classNameBindings: ['isActive:is-active', 'sortDescending:desc:asc'], - - isActive: computed('currentProp', 'prop', function() { + @computed('currentProp', 'prop') + get isActive() { return this.currentProp === this.prop; - }), + } - shouldSortDescending: computed('sortDescending', 'isActive', function() { + @computed('sortDescending', 'isActive') + get shouldSortDescending() { return !this.isActive || !this.sortDescending; - }), -}); + } +} diff --git a/ui/app/components/list-table/table-body.js b/ui/app/components/list-table/table-body.js index 782917851..599a80a20 100644 --- a/ui/app/components/list-table/table-body.js +++ b/ui/app/components/list-table/table-body.js @@ -1,5 +1,7 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'tbody', -}); +@classic +@tagName('tbody') +export default class TableBody extends Component {} diff --git a/ui/app/components/list-table/table-head.js b/ui/app/components/list-table/table-head.js index 92a17d670..e33044d1b 100644 --- a/ui/app/components/list-table/table-head.js +++ b/ui/app/components/list-table/table-head.js @@ -1,5 +1,7 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'thead', -}); +@classic +@tagName('thead') +export default class TableHead extends Component {} diff --git a/ui/app/components/multi-select-dropdown.js b/ui/app/components/multi-select-dropdown.js index 53e438d95..a2783f736 100644 --- a/ui/app/components/multi-select-dropdown.js +++ b/ui/app/components/multi-select-dropdown.js @@ -1,6 +1,9 @@ import Component from '@ember/component'; +import { action } from '@ember/object'; import { computed as overridable } from 'ember-overridable-computed'; import { run } from '@ember/runloop'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; const TAB = 9; const ESC = 27; @@ -8,93 +11,94 @@ const SPACE = 32; const ARROW_UP = 38; const ARROW_DOWN = 40; -export default Component.extend({ - classNames: ['dropdown'], +@classic +@classNames('dropdown') +export default class MultiSelectDropdown extends Component { + @overridable(() => []) options; + @overridable(() => []) selection; - options: overridable(() => []), - selection: overridable(() => []), + onSelect() {} - onSelect() {}, - - isOpen: false, - dropdown: null, + isOpen = false; + dropdown = null; capture(dropdown) { // It's not a good idea to grab a dropdown reference like this, but it's necessary // in order to invoke dropdown.actions.close in traverseList as well as // dropdown.actions.reposition when the label or selection length changes. this.set('dropdown', dropdown); - }, + } didReceiveAttrs() { const dropdown = this.dropdown; if (this.isOpen && dropdown) { run.scheduleOnce('afterRender', this, this.repositionDropdown); } - }, + } repositionDropdown() { this.dropdown.actions.reposition(); - }, + } - actions: { - toggle({ key }) { - const newSelection = this.selection.slice(); - if (newSelection.includes(key)) { - newSelection.removeObject(key); - } else { - newSelection.addObject(key); - } - this.onSelect(newSelection); - }, + @action + toggle({ key }) { + const newSelection = this.selection.slice(); + if (newSelection.includes(key)) { + newSelection.removeObject(key); + } else { + newSelection.addObject(key); + } + this.onSelect(newSelection); + } - openOnArrowDown(dropdown, e) { - this.capture(dropdown); + @action + openOnArrowDown(dropdown, e) { + this.capture(dropdown); - if (!this.isOpen && e.keyCode === ARROW_DOWN) { - dropdown.actions.open(e); - e.preventDefault(); - } else if (this.isOpen && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) { - const optionsId = this.element.querySelector('.dropdown-trigger').getAttribute('aria-owns'); - const firstElement = document.querySelector(`#${optionsId} .dropdown-option`); + if (!this.isOpen && e.keyCode === ARROW_DOWN) { + dropdown.actions.open(e); + e.preventDefault(); + } else if (this.isOpen && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) { + const optionsId = this.element.querySelector('.dropdown-trigger').getAttribute('aria-owns'); + const firstElement = document.querySelector(`#${optionsId} .dropdown-option`); - if (firstElement) { - firstElement.focus(); - e.preventDefault(); - } - } - }, - - traverseList(option, e) { - if (e.keyCode === ESC) { - // Close the dropdown - const dropdown = this.dropdown; - if (dropdown) { - dropdown.actions.close(e); - // Return focus to the trigger so tab works as expected - const trigger = this.element.querySelector('.dropdown-trigger'); - if (trigger) trigger.focus(); - e.preventDefault(); - this.set('dropdown', null); - } - } else if (e.keyCode === ARROW_UP) { - // previous item - const prev = e.target.previousElementSibling; - if (prev) { - prev.focus(); - e.preventDefault(); - } - } else if (e.keyCode === ARROW_DOWN) { - // next item - const next = e.target.nextElementSibling; - if (next) { - next.focus(); - e.preventDefault(); - } - } else if (e.keyCode === SPACE) { - this.send('toggle', option); + if (firstElement) { + firstElement.focus(); e.preventDefault(); } - }, - }, -}); + } + } + + @action + traverseList(option, e) { + if (e.keyCode === ESC) { + // Close the dropdown + const dropdown = this.dropdown; + if (dropdown) { + dropdown.actions.close(e); + // Return focus to the trigger so tab works as expected + const trigger = this.element.querySelector('.dropdown-trigger'); + if (trigger) trigger.focus(); + e.preventDefault(); + this.set('dropdown', null); + } + } else if (e.keyCode === ARROW_UP) { + // previous item + const prev = e.target.previousElementSibling; + if (prev) { + prev.focus(); + e.preventDefault(); + } + } else if (e.keyCode === ARROW_DOWN) { + // next item + const next = e.target.nextElementSibling; + if (next) { + next.focus(); + e.preventDefault(); + } + } else if (e.keyCode === SPACE) { + this.send('toggle', option); + e.preventDefault(); + } + } +} diff --git a/ui/app/components/page-layout.js b/ui/app/components/page-layout.js index 722b5f9eb..8c011d70f 100644 --- a/ui/app/components/page-layout.js +++ b/ui/app/components/page-layout.js @@ -1,7 +1,9 @@ import Component from '@ember/component'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - classNames: ['page-layout'], - - isGutterOpen: false, -}); +@classic +@classNames('page-layout') +export default class PageLayout extends Component { + isGutterOpen = false; +} diff --git a/ui/app/components/placement-failure.js b/ui/app/components/placement-failure.js index b8052235f..0b2e698ce 100644 --- a/ui/app/components/placement-failure.js +++ b/ui/app/components/placement-failure.js @@ -1,10 +1,12 @@ import Component from '@ember/component'; import { or } from '@ember/object/computed'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ +@classic +export default class PlacementFailure extends Component { // Either provide a taskGroup or a failedTGAlloc - taskGroup: null, - failedTGAlloc: null, + taskGroup = null; + failedTGAlloc = null; - placementFailures: or('taskGroup.placementFailures', 'failedTGAlloc'), -}); + @or('taskGroup.placementFailures', 'failedTGAlloc') placementFailures; +} diff --git a/ui/app/components/plugin-allocation-row.js b/ui/app/components/plugin-allocation-row.js index 6c2a65d82..d27bf09fc 100644 --- a/ui/app/components/plugin-allocation-row.js +++ b/ui/app/components/plugin-allocation-row.js @@ -1,14 +1,16 @@ import AllocationRow from 'nomad-ui/components/allocation-row'; +import classic from 'ember-classic-decorator'; -export default AllocationRow.extend({ - pluginAllocation: null, - allocation: null, +@classic +export default class PluginAllocationRow extends AllocationRow { + pluginAllocation = null; + allocation = null; didReceiveAttrs() { // Allocation is always set through pluginAllocation this.set('allocation', null); this.setAllocation(); - }, + } // The allocation for the plugin's controller or storage plugin needs // to be imperatively fetched since these plugins are Fragments which @@ -21,5 +23,5 @@ export default AllocationRow.extend({ this.updateStatsTracker(); } } - }, -}); + } +} diff --git a/ui/app/components/popover-menu.js b/ui/app/components/popover-menu.js index 20e5fc638..939e940b7 100644 --- a/ui/app/components/popover-menu.js +++ b/ui/app/components/popover-menu.js @@ -1,5 +1,8 @@ import Component from '@ember/component'; +import { action } from '@ember/object'; import { run } from '@ember/runloop'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; const TAB = 9; const ARROW_DOWN = 40; @@ -11,49 +14,48 @@ const FOCUSABLE = [ '[tabindex]:not([disabled]):not([tabindex="-1"])', ].join(', '); -export default Component.extend({ - classNames: ['popover'], +@classic +@classNames('popover') +export default class PopoverMenu extends Component { + triggerClass = ''; + isOpen = false; + isDisabled = false; + label = ''; - triggerClass: '', - isOpen: false, - isDisabled: false, - label: '', - - dropdown: null, + dropdown = null; capture(dropdown) { // It's not a good idea to grab a dropdown reference like this, but it's necessary // in order to invoke dropdown.actions.close in traverseList as well as // dropdown.actions.reposition when the label or selection length changes. this.set('dropdown', dropdown); - }, + } didReceiveAttrs() { const dropdown = this.dropdown; if (this.isOpen && dropdown) { run.scheduleOnce('afterRender', this, this.repositionDropdown); } - }, + } repositionDropdown() { this.dropdown.actions.reposition(); - }, + } - actions: { - openOnArrowDown(dropdown, e) { - if (!this.isOpen && e.keyCode === ARROW_DOWN) { - dropdown.actions.open(e); + @action + openOnArrowDown(dropdown, e) { + if (!this.isOpen && e.keyCode === ARROW_DOWN) { + dropdown.actions.open(e); + e.preventDefault(); + } else if (this.isOpen && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) { + const optionsId = this.element.querySelector('.popover-trigger').getAttribute('aria-owns'); + const popoverContentEl = document.querySelector(`#${optionsId}`); + const firstFocusableElement = popoverContentEl.querySelector(FOCUSABLE); + + if (firstFocusableElement) { + firstFocusableElement.focus(); e.preventDefault(); - } else if (this.isOpen && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) { - const optionsId = this.element.querySelector('.popover-trigger').getAttribute('aria-owns'); - const popoverContentEl = document.querySelector(`#${optionsId}`); - const firstFocusableElement = popoverContentEl.querySelector(FOCUSABLE); - - if (firstFocusableElement) { - firstFocusableElement.focus(); - e.preventDefault(); - } } - }, - }, -}); + } + } +} diff --git a/ui/app/components/primary-metric.js b/ui/app/components/primary-metric.js index 3e017d57b..c62141ae9 100644 --- a/ui/app/components/primary-metric.js +++ b/ui/app/components/primary-metric.js @@ -3,49 +3,54 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; import { computed } from '@ember/object'; import { task, timeout } from 'ember-concurrency'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - token: service(), - statsTrackersRegistry: service('stats-trackers-registry'), - - classNames: ['primary-metric'], +@classic +@classNames('primary-metric') +export default class PrimaryMetric extends Component { + @service token; + @service('stats-trackers-registry') statsTrackersRegistry; // One of Node, Allocation, or TaskState - resource: null, + resource = null; // cpu or memory - metric: null, + metric = null; - 'data-test-primary-metric': true, + 'data-test-primary-metric' = true; // An instance of a StatsTracker. An alternative interface to resource - tracker: computed('trackedResource', 'type', function() { + @computed('trackedResource', 'type') + get tracker() { const resource = this.trackedResource; return this.statsTrackersRegistry.getTracker(resource); - }), + } - type: computed('resource', function() { + @computed('resource') + get type() { const resource = this.resource; return resource && resource.constructor.modelName; - }), + } - trackedResource: computed('resource', 'type', function() { + @computed('resource', 'type') + get trackedResource() { // TaskStates use the allocation stats tracker - return this.type === 'task-state' - ? this.get('resource.allocation') - : this.resource; - }), + return this.type === 'task-state' ? this.get('resource.allocation') : this.resource; + } - metricLabel: computed('metric', function() { + @computed('metric') + get metricLabel() { const metric = this.metric; const mappings = { cpu: 'CPU', memory: 'Memory', }; return mappings[metric] || metric; - }), + } - data: computed('resource', 'metric', 'type', function() { + @computed('resource', 'metric', 'type') + get data() { if (!this.tracker) return []; const metric = this.metric; @@ -56,9 +61,10 @@ export default Component.extend({ } return this.get(`tracker.${metric}`); - }), + } - reservedAmount: computed('resource', 'metric', 'type', function() { + @computed('resource', 'metric', 'type') + get reservedAmount() { const metricProperty = this.metric === 'cpu' ? 'reservedCPU' : 'reservedMemory'; if (this.type === 'task-state') { @@ -67,9 +73,10 @@ export default Component.extend({ } return this.get(`tracker.${metricProperty}`); - }), + } - chartClass: computed('metric', function() { + @computed('metric') + get chartClass() { const metric = this.metric; const mappings = { cpu: 'is-info', @@ -77,23 +84,24 @@ export default Component.extend({ }; return mappings[metric] || 'is-primary'; - }), + } - poller: task(function*() { + @task(function*() { do { this.get('tracker.poll').perform(); yield timeout(100); } while (!Ember.testing); - }), + }) + poller; didReceiveAttrs() { if (this.tracker) { this.poller.perform(); } - }, + } willDestroy() { this.poller.cancelAll(); this.get('tracker.signalPause').perform(); - }, -}); + } +} diff --git a/ui/app/components/proxy-tag.js b/ui/app/components/proxy-tag.js index 479865264..ca856391f 100644 --- a/ui/app/components/proxy-tag.js +++ b/ui/app/components/proxy-tag.js @@ -1,5 +1,7 @@ import Component from '@ember/component'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: '', -}); +@classic +@tagName('') +export default class ProxyTag extends Component {} diff --git a/ui/app/components/region-switcher.js b/ui/app/components/region-switcher.js index b9fe868b7..34fc3c98e 100644 --- a/ui/app/components/region-switcher.js +++ b/ui/app/components/region-switcher.js @@ -1,21 +1,24 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { inject as service } from '@ember/service'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - system: service(), - router: service(), - store: service(), +@classic +export default class RegionSwitcher extends Component { + @service system; + @service router; + @service store; - sortedRegions: computed('system.regions', function() { + @computed('system.regions') + get sortedRegions() { return this.get('system.regions') .toArray() .sort(); - }), + } gotoRegion(region) { this.router.transitionTo('jobs', { queryParams: { region }, }); - }, -}); + } +} diff --git a/ui/app/components/reschedule-event-row.js b/ui/app/components/reschedule-event-row.js index 6fc566051..218241d1b 100644 --- a/ui/app/components/reschedule-event-row.js +++ b/ui/app/components/reschedule-event-row.js @@ -1,20 +1,24 @@ import Component from '@ember/component'; import { computed as overridable } from 'ember-overridable-computed'; import { inject as service } from '@ember/service'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - store: service(), - tagName: '', +@classic +@tagName('') +export default class RescheduleEventRow extends Component { + @service store; // When given a string, the component will fetch the allocation - allocationId: null, + allocationId = null; // An allocation can also be provided directly - allocation: overridable('allocationId', function() { + @overridable('allocationId', function() { return this.store.findRecord('allocation', this.allocationId); - }), + }) + allocation; - time: null, - linkToAllocation: true, - label: '', -}); + time = null; + linkToAllocation = true; + label = ''; +} diff --git a/ui/app/components/search-box.js b/ui/app/components/search-box.js index 25cf7715a..94de9724b 100644 --- a/ui/app/components/search-box.js +++ b/ui/app/components/search-box.js @@ -1,34 +1,37 @@ import { reads } from '@ember/object/computed'; import Component from '@ember/component'; +import { action } from '@ember/object'; import { run } from '@ember/runloop'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ +@classic +@classNames('search-box', 'field', 'has-addons') +export default class SearchBox extends Component { // Passed to the component (mutable) - searchTerm: null, + searchTerm = null; // Used as a debounce buffer - _searchTerm: reads('searchTerm'), + @reads('searchTerm') _searchTerm; // Used to throttle sets to searchTerm - debounce: 150, + debounce = 150; // A hook that's called when the search value changes - onChange() {}, + onChange() {} - classNames: ['search-box', 'field', 'has-addons'], + @action + setSearchTerm(e) { + this.set('_searchTerm', e.target.value); + run.debounce(this, updateSearch, this.debounce); + } - actions: { - setSearchTerm(e) { - this.set('_searchTerm', e.target.value); - run.debounce(this, updateSearch, this.debounce); - }, - - clear() { - this.set('_searchTerm', ''); - run.debounce(this, updateSearch, this.debounce); - }, - }, -}); + @action + clear() { + this.set('_searchTerm', ''); + run.debounce(this, updateSearch, this.debounce); + } +} function updateSearch() { const newTerm = this._searchTerm; diff --git a/ui/app/components/server-agent-row.js b/ui/app/components/server-agent-row.js index 3bc395395..7118a51f5 100644 --- a/ui/app/components/server-agent-row.js +++ b/ui/app/components/server-agent-row.js @@ -3,20 +3,24 @@ import { alias } from '@ember/object/computed'; import Component from '@ember/component'; import { computed } from '@ember/object'; import { lazyClick } from '../helpers/lazy-click'; +import { classNames, classNameBindings, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ +@classic +@tagName('tr') +@classNames('server-agent-row', 'is-interactive') +@classNameBindings('isActive:is-active') +export default class ServerAgentRow extends Component { // TODO Switch back to the router service once the service behaves more like Route // https://github.com/emberjs/ember.js/issues/15801 // router: inject.service('router'), - _router: service('-routing'), - router: alias('_router.router'), + @service('-routing') _router; + @alias('_router.router') router; - tagName: 'tr', - classNames: ['server-agent-row', 'is-interactive'], - classNameBindings: ['isActive:is-active'], + agent = null; - agent: null, - isActive: computed('agent', 'router.currentURL', function() { + @computed('agent', 'router.currentURL') + get isActive() { // TODO Switch back to the router service once the service behaves more like Route // https://github.com/emberjs/ember.js/issues/15801 // const targetURL = this.get('router').urlFor('servers.server', this.get('agent')); @@ -30,10 +34,10 @@ export default Component.extend({ // Account for potential URI encoding return currentURL.replace(/%40/g, '@') === targetURL.replace(/%40/g, '@'); - }), + } click() { const transition = () => this.router.transitionTo('servers.server', this.agent); lazyClick([transition, event]); - }, -}); + } +} diff --git a/ui/app/components/stats-time-series.js b/ui/app/components/stats-time-series.js index 932d10f84..0bce65616 100644 --- a/ui/app/components/stats-time-series.js +++ b/ui/app/components/stats-time-series.js @@ -6,24 +6,27 @@ import d3Scale from 'd3-scale'; import d3Array from 'd3-array'; import LineChart from 'nomad-ui/components/line-chart'; import formatDuration from 'nomad-ui/utils/format-duration'; +import classic from 'ember-classic-decorator'; -export default LineChart.extend({ - xProp: 'timestamp', - yProp: 'percent', - timeseries: true, +@classic +export default class StatsTimeSeries extends LineChart { + xProp = 'timestamp'; + yProp = 'percent'; + timeseries = true; xFormat() { return d3TimeFormat.timeFormat('%H:%M:%S'); - }, + } yFormat() { return d3Format.format('.1~%'); - }, + } // Specific a11y descriptors - title: 'Stats Time Series Chart', + title = 'Stats Time Series Chart'; - description: computed('data.[]', 'xProp', 'yProp', function() { + @computed('data.[]', 'xProp', 'yProp') + get description() { const { xProp, yProp, data } = this; const yRange = d3Array.extent(data, d => d[yProp]); const xRange = d3Array.extent(data, d => d[xProp]); @@ -31,10 +34,13 @@ export default LineChart.extend({ const duration = formatDuration(xRange[1] - xRange[0], 'ms', true); - return `Time series data for the last ${duration}, with values ranging from ${yFormatter(yRange[0])} to ${yFormatter(yRange[1])}`; - }), + return `Time series data for the last ${duration}, with values ranging from ${yFormatter( + yRange[0] + )} to ${yFormatter(yRange[1])}`; + } - xScale: computed('data.[]', 'xProp', 'timeseries', 'yAxisOffset', function() { + @computed('data.[]', 'xProp', 'timeseries', 'yAxisOffset') + get xScale() { const xProp = this.xProp; const scale = this.timeseries ? d3Scale.scaleTime() : d3Scale.scaleLinear(); const data = this.data; @@ -48,9 +54,10 @@ export default LineChart.extend({ scale.rangeRound([10, this.yAxisOffset]).domain(extent); return scale; - }), + } - yScale: computed('data.[]', 'yProp', 'xAxisOffset', function() { + @computed('data.[]', 'yProp', 'xAxisOffset') + get yScale() { const yProp = this.yProp; const yValues = (this.data || []).mapBy(yProp); @@ -63,5 +70,5 @@ export default LineChart.extend({ .scaleLinear() .rangeRound([this.xAxisOffset, 10]) .domain([Math.min(0, low), Math.max(1, high)]); - }), -}); + } +} diff --git a/ui/app/components/streaming-file.js b/ui/app/components/streaming-file.js index b35963574..1386dc4c0 100644 --- a/ui/app/components/streaming-file.js +++ b/ui/app/components/streaming-file.js @@ -2,15 +2,18 @@ import Component from '@ember/component'; import { run } from '@ember/runloop'; import { task } from 'ember-concurrency'; import WindowResizable from 'nomad-ui/mixins/window-resizable'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend(WindowResizable, { - tagName: 'pre', - classNames: ['cli-window'], - 'data-test-log-cli': true, +@classic +@tagName('pre') +@classNames('cli-window') +export default class StreamingFile extends Component.extend(WindowResizable) { + 'data-test-log-cli' = true; - mode: 'streaming', // head, tail, streaming - isStreaming: true, - logger: null, + mode = 'streaming'; // head, tail, streaming + isStreaming = true; + logger = null; didReceiveAttrs() { if (!this.logger) { @@ -18,7 +21,7 @@ export default Component.extend(WindowResizable, { } run.scheduleOnce('actions', this, this.performTask); - }, + } performTask() { switch (this.mode) { @@ -36,15 +39,15 @@ export default Component.extend(WindowResizable, { } break; } - }, + } didInsertElement() { this.fillAvailableHeight(); - }, + } windowResizeHandler() { run.once(this, this.fillAvailableHeight); - }, + } fillAvailableHeight() { // This math is arbitrary and far from bulletproof, but the UX @@ -52,21 +55,23 @@ export default Component.extend(WindowResizable, { const margins = 30; // Account for padding and margin on either side of the CLI const cliWindow = this.element; cliWindow.style.height = `${window.innerHeight - cliWindow.offsetTop - margins}px`; - }, + } - head: task(function*() { + @task(function*() { yield this.get('logger.gotoHead').perform(); run.scheduleOnce('afterRender', this, this.scrollToTop); - }), + }) + head; scrollToTop() { this.element.scrollTop = 0; - }, + } - tail: task(function*() { + @task(function*() { yield this.get('logger.gotoTail').perform(); run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition, [true]); - }), + }) + tail; synchronizeScrollPosition(force = false) { const cliWindow = this.element; @@ -74,9 +79,9 @@ export default Component.extend(WindowResizable, { // If the window is approximately scrolled to the bottom, follow the log cliWindow.scrollTop = cliWindow.scrollHeight; } - }, + } - stream: task(function*() { + @task(function*() { // Force the scroll position to the bottom of the window when starting streaming this.logger.one('tick', () => { run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition, [true]); @@ -87,13 +92,14 @@ export default Component.extend(WindowResizable, { yield this.logger.startStreaming(); this.logger.off('tick', this, 'scheduleScrollSynchronization'); - }), + }) + stream; scheduleScrollSynchronization() { run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition); - }, + } willDestroy() { this.logger.stop(); - }, -}); + } +} diff --git a/ui/app/components/task-group-row.js b/ui/app/components/task-group-row.js index 9ee2cbe5f..399d90167 100644 --- a/ui/app/components/task-group-row.js +++ b/ui/app/components/task-group-row.js @@ -1,16 +1,17 @@ import Component from '@ember/component'; import { lazyClick } from '../helpers/lazy-click'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'tr', +@classic +@tagName('tr') +@classNames('task-group-row', 'is-interactive') +export default class TaskGroupRow extends Component { + taskGroup = null; - classNames: ['task-group-row', 'is-interactive'], - - taskGroup: null, - - onClick() {}, + onClick() {} click(event) { lazyClick([this.onClick, event]); - }, -}); + } +} diff --git a/ui/app/components/task-log.js b/ui/app/components/task-log.js index deaef2c89..61e74b26a 100644 --- a/ui/app/components/task-log.js +++ b/ui/app/components/task-log.js @@ -1,49 +1,53 @@ import { inject as service } from '@ember/service'; import Component from '@ember/component'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import RSVP from 'rsvp'; import { logger } from 'nomad-ui/utils/classes/log'; import timeout from 'nomad-ui/utils/timeout'; import { AbortController } from 'fetch'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - token: service(), +@classic +@classNames('boxed-section', 'task-log') +export default class TaskLog extends Component { + @service token; - classNames: ['boxed-section', 'task-log'], - - allocation: null, - task: null, + allocation = null; + task = null; // When true, request logs from the server agent - useServer: false, + useServer = false; // When true, logs cannot be fetched from either the client or the server - noConnection: false, + noConnection = false; - clientTimeout: 1000, - serverTimeout: 5000, + clientTimeout = 1000; + serverTimeout = 5000; - isStreaming: true, - streamMode: 'streaming', + isStreaming = true; + streamMode = 'streaming'; - mode: 'stdout', + mode = 'stdout'; - logUrl: computed('allocation.{id,node.httpAddr}', 'useServer', function() { + @computed('allocation.{id,node.httpAddr}', 'useServer') + get logUrl() { const address = this.get('allocation.node.httpAddr'); const allocation = this.get('allocation.id'); const url = `/v1/client/fs/logs/${allocation}`; return this.useServer ? url : `//${address}${url}`; - }), + } - logParams: computed('task', 'mode', function() { + @computed('task', 'mode') + get logParams() { return { task: this.task, type: this.mode, }; - }), + } - logger: logger('logUrl', 'logParams', function logFetch() { + @logger('logUrl', 'logParams', function logFetch() { // If the log request can't settle in one second, the client // must be unavailable and the server should be used instead @@ -71,28 +75,36 @@ export default Component.extend({ throw error; } ); - }), + }) + logger; - actions: { - setMode(mode) { - if (this.mode === mode) return; - this.logger.stop(); - this.set('mode', mode); - }, - toggleStream() { - this.set('streamMode', 'streaming'); - this.toggleProperty('isStreaming'); - }, - gotoHead() { - this.set('streamMode', 'head'); - this.set('isStreaming', false); - }, - gotoTail() { - this.set('streamMode', 'tail'); - this.set('isStreaming', false); - }, - failoverToServer() { - this.set('useServer', true); - }, - }, -}); + @action + setMode(mode) { + if (this.mode === mode) return; + this.logger.stop(); + this.set('mode', mode); + } + + @action + toggleStream() { + this.set('streamMode', 'streaming'); + this.toggleProperty('isStreaming'); + } + + @action + gotoHead() { + this.set('streamMode', 'head'); + this.set('isStreaming', false); + } + + @action + gotoTail() { + this.set('streamMode', 'tail'); + this.set('isStreaming', false); + } + + @action + failoverToServer() { + this.set('useServer', true); + } +} diff --git a/ui/app/components/task-row.js b/ui/app/components/task-row.js index 4901d1742..feeb36ef1 100644 --- a/ui/app/components/task-row.js +++ b/ui/app/components/task-row.js @@ -5,47 +5,52 @@ import { computed } from '@ember/object'; import { alias } from '@ember/object/computed'; import { task, timeout } from 'ember-concurrency'; import { lazyClick } from '../helpers/lazy-click'; +import { classNames, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - store: service(), - token: service(), - statsTrackersRegistry: service('stats-trackers-registry'), +@classic +@tagName('tr') +@classNames('task-row', 'is-interactive') +export default class TaskRow extends Component { + @service store; + @service token; + @service('stats-trackers-registry') statsTrackersRegistry; - tagName: 'tr', - classNames: ['task-row', 'is-interactive'], - - task: null, + task = null; // Internal state - statsError: false, + statsError = false; - enablePolling: computed(function() { + @computed + get enablePolling() { return !Ember.testing; - }), + } // Since all tasks for an allocation share the same tracker, use the registry - stats: computed('task', 'task.isRunning', function() { + @computed('task', 'task.isRunning') + get stats() { if (!this.get('task.isRunning')) return; return this.statsTrackersRegistry.getTracker(this.get('task.allocation')); - }), + } - taskStats: computed('task.name', 'stats.tasks.[]', function() { + @computed('task.name', 'stats.tasks.[]') + get taskStats() { if (!this.stats) return; return this.get('stats.tasks').findBy('task', this.get('task.name')); - }), + } - cpu: alias('taskStats.cpu.lastObject'), - memory: alias('taskStats.memory.lastObject'), + @alias('taskStats.cpu.lastObject') cpu; + @alias('taskStats.memory.lastObject') memory; - onClick() {}, + onClick() {} click(event) { lazyClick([this.onClick, event]); - }, + } - fetchStats: task(function*() { + @(task(function*() { do { if (this.stats) { try { @@ -58,7 +63,8 @@ export default Component.extend({ yield timeout(500); } while (this.enablePolling); - }).drop(), + }).drop()) + fetchStats; didReceiveAttrs() { const allocation = this.get('task.allocation'); @@ -68,5 +74,5 @@ export default Component.extend({ } else { this.fetchStats.cancelAll(); } - }, -}); + } +} diff --git a/ui/app/components/task-subnav.js b/ui/app/components/task-subnav.js index 0aab1e6f4..702b19caa 100644 --- a/ui/app/components/task-subnav.js +++ b/ui/app/components/task-subnav.js @@ -1,14 +1,20 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; import { equal, or } from '@ember/object/computed'; +import { tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - router: service(), +@classic +@tagName('') +export default class TaskSubnav extends Component { + @service router; - tagName: '', + @equal('router.currentRouteName', 'allocations.allocation.task.fs') + fsIsActive; - fsIsActive: equal('router.currentRouteName', 'allocations.allocation.task.fs'), - fsRootIsActive: equal('router.currentRouteName', 'allocations.allocation.task.fs-root'), + @equal('router.currentRouteName', 'allocations.allocation.task.fs-root') + fsRootIsActive; - filesLinkActive: or('fsIsActive', 'fsRootIsActive'), -}); + @or('fsIsActive', 'fsRootIsActive') + filesLinkActive; +} diff --git a/ui/app/components/toggle.js b/ui/app/components/toggle.js index e4becf153..6701f64f9 100644 --- a/ui/app/components/toggle.js +++ b/ui/app/components/toggle.js @@ -1,13 +1,15 @@ import Component from '@ember/component'; +import { classNames, classNameBindings, tagName } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - tagName: 'label', - classNames: ['toggle'], - classNameBindings: ['isDisabled:is-disabled', 'isActive:is-active'], +@classic +@tagName('label') +@classNames('toggle') +@classNameBindings('isDisabled:is-disabled', 'isActive:is-active') +export default class Toggle extends Component { + 'data-test-label' = true; - 'data-test-label': true, - - isActive: false, - isDisabled: false, - onToggle() {}, -}); + isActive = false; + isDisabled = false; + onToggle() {} +} diff --git a/ui/app/components/two-step-button.js b/ui/app/components/two-step-button.js index 15fe6aad6..a3bb4f5f0 100644 --- a/ui/app/components/two-step-button.js +++ b/ui/app/components/two-step-button.js @@ -1,51 +1,58 @@ import Component from '@ember/component'; +import { action } from '@ember/object'; import { next } from '@ember/runloop'; import { equal } from '@ember/object/computed'; import { task, waitForEvent } from 'ember-concurrency'; import RSVP from 'rsvp'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - classNames: ['two-step-button'], +@classic +@classNames('two-step-button') +export default class TwoStepButton extends Component { + idleText = ''; + cancelText = ''; + confirmText = ''; + confirmationMessage = ''; + awaitingConfirmation = false; + disabled = false; + alignRight = false; + isInfoAction = false; + onConfirm() {} + onCancel() {} - idleText: '', - cancelText: '', - confirmText: '', - confirmationMessage: '', - awaitingConfirmation: false, - disabled: false, - alignRight: false, - isInfoAction: false, - onConfirm() {}, - onCancel() {}, + state = 'idle'; + @equal('state', 'idle') isIdle; + @equal('state', 'prompt') isPendingConfirmation; - state: 'idle', - isIdle: equal('state', 'idle'), - isPendingConfirmation: equal('state', 'prompt'), - - cancelOnClickOutside: task(function*() { + @task(function*() { while (true) { let ev = yield waitForEvent(document.body, 'click'); if (!this.element.contains(ev.target) && !this.awaitingConfirmation) { this.send('setToIdle'); } } - }), + }) + cancelOnClickOutside; - actions: { - setToIdle() { - this.set('state', 'idle'); - this.cancelOnClickOutside.cancelAll(); - }, - promptForConfirmation() { - this.set('state', 'prompt'); - next(() => { - this.cancelOnClickOutside.perform(); - }); - }, - confirm() { - RSVP.resolve(this.onConfirm()).then(() => { - this.send('setToIdle'); - }); - }, - }, -}); + @action + setToIdle() { + this.set('state', 'idle'); + this.cancelOnClickOutside.cancelAll(); + } + + @action + promptForConfirmation() { + this.set('state', 'prompt'); + next(() => { + this.cancelOnClickOutside.perform(); + }); + } + + @action + confirm() { + RSVP.resolve(this.onConfirm()).then(() => { + this.send('setToIdle'); + }); + } +} diff --git a/ui/app/controllers/allocations/allocation/fs-root.js b/ui/app/controllers/allocations/allocation/fs-root.js index 2297a800e..5f732bf9c 100644 --- a/ui/app/controllers/allocations/allocation/fs-root.js +++ b/ui/app/controllers/allocations/allocation/fs-root.js @@ -1,3 +1,3 @@ import FSController from './fs'; -export default FSController.extend(); +export default class FsRootController extends FSController {} diff --git a/ui/app/controllers/allocations/allocation/fs.js b/ui/app/controllers/allocations/allocation/fs.js index 745eeb9d0..cf59dc753 100644 --- a/ui/app/controllers/allocations/allocation/fs.js +++ b/ui/app/controllers/allocations/allocation/fs.js @@ -1,22 +1,23 @@ import Controller from '@ember/controller'; import { computed } from '@ember/object'; -export default Controller.extend({ - queryParams: { +export default class FsController extends Controller { + queryParams = { sortProperty: 'sort', sortDescending: 'desc', - }, + }; - sortProperty: 'Name', - sortDescending: false, + sortProperty = 'Name'; + sortDescending = false; - path: null, - allocation: null, - directoryEntries: null, - isFile: null, - stat: null, + path = null; + allocation = null; + directoryEntries = null; + isFile = null; + stat = null; - pathWithLeadingSlash: computed('path', function() { + @computed('path') + get pathWithLeadingSlash() { const path = this.path; if (path.startsWith('/')) { @@ -24,5 +25,5 @@ export default Controller.extend({ } else { return `/${path}`; } - }), -}); + } +} diff --git a/ui/app/controllers/allocations/allocation/task/fs-root.js b/ui/app/controllers/allocations/allocation/task/fs-root.js index 2297a800e..5f732bf9c 100644 --- a/ui/app/controllers/allocations/allocation/task/fs-root.js +++ b/ui/app/controllers/allocations/allocation/task/fs-root.js @@ -1,3 +1,3 @@ import FSController from './fs'; -export default FSController.extend(); +export default class FsRootController extends FSController {} diff --git a/ui/app/controllers/allocations/allocation/task/fs.js b/ui/app/controllers/allocations/allocation/task/fs.js index a0b0720cc..cf27a5112 100644 --- a/ui/app/controllers/allocations/allocation/task/fs.js +++ b/ui/app/controllers/allocations/allocation/task/fs.js @@ -1,22 +1,23 @@ import Controller from '@ember/controller'; import { computed } from '@ember/object'; -export default Controller.extend({ - queryParams: { +export default class FsController extends Controller { + queryParams = { sortProperty: 'sort', sortDescending: 'desc', - }, + }; - sortProperty: 'Name', - sortDescending: false, + sortProperty = 'Name'; + sortDescending = false; - path: null, - taskState: null, - directoryEntries: null, - isFile: null, - stat: null, + path = null; + taskState = null; + directoryEntries = null; + isFile = null; + stat = null; - pathWithLeadingSlash: computed('path', function() { + @computed('path') + get pathWithLeadingSlash() { const path = this.path; if (path.startsWith('/')) { @@ -24,5 +25,5 @@ export default Controller.extend({ } else { return `/${path}`; } - }), -}); + } +} diff --git a/ui/app/controllers/allocations/allocation/task/index.js b/ui/app/controllers/allocations/allocation/task/index.js index 84a62c675..994f0319f 100644 --- a/ui/app/controllers/allocations/allocation/task/index.js +++ b/ui/app/controllers/allocations/allocation/task/index.js @@ -3,19 +3,25 @@ import { computed } from '@ember/object'; import { computed as overridable } from 'ember-overridable-computed'; import { alias } from '@ember/object/computed'; import { task } from 'ember-concurrency'; +import classic from 'ember-classic-decorator'; -export default Controller.extend({ - otherTaskStates: computed('model.task.taskGroup.tasks.@each.name', function() { +@classic +export default class IndexController extends Controller { + @computed('model.task.taskGroup.tasks.@each.name') + get otherTaskStates() { const taskName = this.model.task.name; return this.model.allocation.states.rejectBy('name', taskName); - }), + } - prestartTaskStates: computed('otherTaskStates.@each.lifecycle', function() { + @computed('otherTaskStates.@each.lifecycle') + get prestartTaskStates() { return this.otherTaskStates.filterBy('task.lifecycle'); - }), + } - network: alias('model.resources.networks.firstObject'), - ports: computed('network.{reservedPorts.[],dynamicPorts.[]}', function() { + @alias('model.resources.networks.firstObject') network; + + @computed('network.{reservedPorts.[],dynamicPorts.[]}') + get ports() { return (this.get('network.reservedPorts') || []) .map(port => ({ name: port.Label, @@ -30,18 +36,19 @@ export default Controller.extend({ })) ) .sortBy('name'); - }), + } - error: overridable(() => { + @overridable(() => { // { title, description } return null; - }), + }) + error; onDismiss() { this.set('error', null); - }, + } - restartTask: task(function*() { + @task(function*() { try { yield this.model.restart(); } catch (err) { @@ -50,5 +57,6 @@ export default Controller.extend({ description: 'Your ACL token does not grant allocation lifecycle permissions.', }); } - }), -}); + }) + restartTask; +} diff --git a/ui/app/controllers/application.js b/ui/app/controllers/application.js index 0a1597503..fcebfdcd1 100644 --- a/ui/app/controllers/application.js +++ b/ui/app/controllers/application.js @@ -2,49 +2,59 @@ import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; import { run } from '@ember/runloop'; -import { observer, computed } from '@ember/object'; +import { observes } from '@ember-decorators/object'; +import { computed } from '@ember/object'; import Ember from 'ember'; import codesForError from '../utils/codes-for-error'; import NoLeaderError from '../utils/no-leader-error'; +import classic from 'ember-classic-decorator'; -export default Controller.extend({ - config: service(), - system: service(), +@classic +export default class ApplicationController extends Controller { + @service config; + @service system; - queryParams: { + queryParams = { region: 'region', - }, + }; - region: null, + region = null; - error: null, + error = null; - errorStr: computed('error', function() { + @computed('error') + get errorStr() { return this.error.toString(); - }), + } - errorCodes: computed('error', function() { + @computed('error') + get errorCodes() { return codesForError(this.error); - }), + } - is403: computed('errorCodes.[]', function() { + @computed('errorCodes.[]') + get is403() { return this.errorCodes.includes('403'); - }), + } - is404: computed('errorCodes.[]', function() { + @computed('errorCodes.[]') + get is404() { return this.errorCodes.includes('404'); - }), + } - is500: computed('errorCodes.[]', function() { + @computed('errorCodes.[]') + get is500() { return this.errorCodes.includes('500'); - }), + } - isNoLeader: computed('error', function() { + @computed('error') + get isNoLeader() { const error = this.error; return error instanceof NoLeaderError; - }), + } - throwError: observer('error', function() { + @observes('error') + throwError() { if (this.get('config.isDev')) { run.next(() => { throw this.error; @@ -55,5 +65,5 @@ export default Controller.extend({ console.warn('UNRECOVERABLE ERROR:', this.error); }); } - }), -}); + } +} diff --git a/ui/app/controllers/clients.js b/ui/app/controllers/clients.js index f4d0631dc..673868f9b 100644 --- a/ui/app/controllers/clients.js +++ b/ui/app/controllers/clients.js @@ -1,5 +1,5 @@ import Controller from '@ember/controller'; -export default Controller.extend({ - isForbidden: false, -}); +export default class ClientsController extends Controller { + isForbidden = false; +} diff --git a/ui/app/controllers/clients/index.js b/ui/app/controllers/clients/index.js index 78de1aa35..0b7f57d15 100644 --- a/ui/app/controllers/clients/index.js +++ b/ui/app/controllers/clients/index.js @@ -2,153 +2,158 @@ import { alias, readOnly } from '@ember/object/computed'; import { inject as service } from '@ember/service'; import Controller, { inject as controller } from '@ember/controller'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import { scheduleOnce } from '@ember/runloop'; import intersection from 'lodash.intersection'; import SortableFactory from 'nomad-ui/mixins/sortable-factory'; import Searchable from 'nomad-ui/mixins/searchable'; import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize'; +import classic from 'ember-classic-decorator'; -export default Controller.extend( - SortableFactory(['id', 'name', 'compositeStatus', 'datacenter']), - Searchable, - { - userSettings: service(), - clientsController: controller('clients'), +@classic +export default class IndexController extends Controller.extend( + SortableFactory(['id', 'name', 'compositeStatus', 'datacenter']), + Searchable + ) { + @service userSettings; + @controller('clients') clientsController; - nodes: alias('model.nodes'), - agents: alias('model.agents'), + @alias('model.nodes') nodes; + @alias('model.agents') agents; - queryParams: { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - qpClass: 'class', - qpState: 'state', - qpDatacenter: 'dc', - qpVolume: 'volume', - }, + queryParams = { + currentPage: 'page', + searchTerm: 'search', + sortProperty: 'sort', + sortDescending: 'desc', + qpClass: 'class', + qpState: 'state', + qpDatacenter: 'dc', + qpVolume: 'volume', + }; - currentPage: 1, - pageSize: readOnly('userSettings.pageSize'), + currentPage = 1; + @readOnly('userSettings.pageSize') pageSize; - sortProperty: 'modifyIndex', - sortDescending: true, + sortProperty = 'modifyIndex'; + sortDescending = true; - searchProps: computed(function() { - return ['id', 'name', 'datacenter']; - }), - - qpClass: '', - qpState: '', - qpDatacenter: '', - qpVolume: '', - - selectionClass: selection('qpClass'), - selectionState: selection('qpState'), - selectionDatacenter: selection('qpDatacenter'), - selectionVolume: selection('qpVolume'), - - optionsClass: computed('nodes.[]', function() { - const classes = Array.from(new Set(this.nodes.mapBy('nodeClass'))) - .compact() - .without(''); - - // Remove any invalid node classes from the query param/selection - scheduleOnce('actions', () => { - // eslint-disable-next-line ember/no-side-effects - this.set('qpClass', serialize(intersection(classes, this.selectionClass))); - }); - - return classes.sort().map(dc => ({ key: dc, label: dc })); - }), - - optionsState: computed(function() { - return [ - { key: 'initializing', label: 'Initializing' }, - { key: 'ready', label: 'Ready' }, - { key: 'down', label: 'Down' }, - { key: 'ineligible', label: 'Ineligible' }, - { key: 'draining', label: 'Draining' }, - ]; - }), - - optionsDatacenter: computed('nodes.[]', function() { - const datacenters = Array.from(new Set(this.nodes.mapBy('datacenter'))).compact(); - - // Remove any invalid datacenters from the query param/selection - scheduleOnce('actions', () => { - // eslint-disable-next-line ember/no-side-effects - this.set('qpDatacenter', serialize(intersection(datacenters, this.selectionDatacenter))); - }); - - return datacenters.sort().map(dc => ({ key: dc, label: dc })); - }), - - optionsVolume: computed('nodes.[]', function() { - const flatten = (acc, val) => acc.concat(val.toArray()); - - const allVolumes = this.nodes.mapBy('hostVolumes').reduce(flatten, []); - const volumes = Array.from(new Set(allVolumes.mapBy('name'))); - - scheduleOnce('actions', () => { - // eslint-disable-next-line ember/no-side-effects - this.set('qpVolume', serialize(intersection(volumes, this.selectionVolume))); - }); - - return volumes.sort().map(volume => ({ key: volume, label: volume })); - }), - - filteredNodes: computed( - 'nodes.[]', - 'selectionClass', - 'selectionState', - 'selectionDatacenter', - 'selectionVolume', - function() { - const { - selectionClass: classes, - selectionState: states, - selectionDatacenter: datacenters, - selectionVolume: volumes, - } = this; - - const onlyIneligible = states.includes('ineligible'); - const onlyDraining = states.includes('draining'); - - // states is a composite of node status and other node states - const statuses = states.without('ineligible').without('draining'); - - return this.nodes.filter(node => { - if (classes.length && !classes.includes(node.get('nodeClass'))) return false; - if (statuses.length && !statuses.includes(node.get('status'))) return false; - if (datacenters.length && !datacenters.includes(node.get('datacenter'))) return false; - if (volumes.length && !node.hostVolumes.find(volume => volumes.includes(volume.name))) - return false; - - if (onlyIneligible && node.get('isEligible')) return false; - if (onlyDraining && !node.get('isDraining')) return false; - - return true; - }); - } - ), - - listToSort: alias('filteredNodes'), - listToSearch: alias('listSorted'), - sortedNodes: alias('listSearched'), - - isForbidden: alias('clientsController.isForbidden'), - - setFacetQueryParam(queryParam, selection) { - this.set(queryParam, serialize(selection)); - }, - - actions: { - gotoNode(node) { - this.transitionToRoute('clients.client', node); - }, - }, + @computed + get searchProps() { + return ['id', 'name', 'datacenter']; } -); + + qpClass = ''; + qpState = ''; + qpDatacenter = ''; + qpVolume = ''; + + @selection('qpClass') selectionClass; + @selection('qpState') selectionState; + @selection('qpDatacenter') selectionDatacenter; + @selection('qpVolume') selectionVolume; + + @computed('nodes.[]') + get optionsClass() { + const classes = Array.from(new Set(this.nodes.mapBy('nodeClass'))) + .compact() + .without(''); + + // Remove any invalid node classes from the query param/selection + scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects + this.set('qpClass', serialize(intersection(classes, this.selectionClass))); + }); + + return classes.sort().map(dc => ({ key: dc, label: dc })); + } + + @computed + get optionsState() { + return [ + { key: 'initializing', label: 'Initializing' }, + { key: 'ready', label: 'Ready' }, + { key: 'down', label: 'Down' }, + { key: 'ineligible', label: 'Ineligible' }, + { key: 'draining', label: 'Draining' }, + ]; + } + + @computed('nodes.[]') + get optionsDatacenter() { + const datacenters = Array.from(new Set(this.nodes.mapBy('datacenter'))).compact(); + + // Remove any invalid datacenters from the query param/selection + scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects + this.set('qpDatacenter', serialize(intersection(datacenters, this.selectionDatacenter))); + }); + + return datacenters.sort().map(dc => ({ key: dc, label: dc })); + } + + @computed('nodes.[]') + get optionsVolume() { + const flatten = (acc, val) => acc.concat(val.toArray()); + + const allVolumes = this.nodes.mapBy('hostVolumes').reduce(flatten, []); + const volumes = Array.from(new Set(allVolumes.mapBy('name'))); + + scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects + this.set('qpVolume', serialize(intersection(volumes, this.selectionVolume))); + }); + + return volumes.sort().map(volume => ({ key: volume, label: volume })); + } + + @computed( + 'nodes.[]', + 'selectionClass', + 'selectionState', + 'selectionDatacenter', + 'selectionVolume' + ) + get filteredNodes() { + const { + selectionClass: classes, + selectionState: states, + selectionDatacenter: datacenters, + selectionVolume: volumes, + } = this; + + const onlyIneligible = states.includes('ineligible'); + const onlyDraining = states.includes('draining'); + + // states is a composite of node status and other node states + const statuses = states.without('ineligible').without('draining'); + + return this.nodes.filter(node => { + if (classes.length && !classes.includes(node.get('nodeClass'))) return false; + if (statuses.length && !statuses.includes(node.get('status'))) return false; + if (datacenters.length && !datacenters.includes(node.get('datacenter'))) return false; + if (volumes.length && !node.hostVolumes.find(volume => volumes.includes(volume.name))) + return false; + + if (onlyIneligible && node.get('isEligible')) return false; + if (onlyDraining && !node.get('isDraining')) return false; + + return true; + }); + } + + @alias('filteredNodes') listToSort; + @alias('listSorted') listToSearch; + @alias('listSearched') sortedNodes; + + @alias('clientsController.isForbidden') isForbidden; + + setFacetQueryParam(queryParam, selection) { + this.set(queryParam, serialize(selection)); + } + + @action + gotoNode(node) { + this.transitionToRoute('clients.client', node); + } +} diff --git a/ui/app/controllers/csi/plugins.js b/ui/app/controllers/csi/plugins.js index f4d0631dc..b9a8c5a54 100644 --- a/ui/app/controllers/csi/plugins.js +++ b/ui/app/controllers/csi/plugins.js @@ -1,5 +1,5 @@ import Controller from '@ember/controller'; -export default Controller.extend({ - isForbidden: false, -}); +export default class PluginsController extends Controller { + isForbidden = false; +} diff --git a/ui/app/controllers/csi/plugins/index.js b/ui/app/controllers/csi/plugins/index.js index 66a888416..2ca4889ea 100644 --- a/ui/app/controllers/csi/plugins/index.js +++ b/ui/app/controllers/csi/plugins/index.js @@ -1,53 +1,56 @@ import { inject as service } from '@ember/service'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import { alias, readOnly } from '@ember/object/computed'; import Controller, { inject as controller } from '@ember/controller'; import SortableFactory from 'nomad-ui/mixins/sortable-factory'; import Searchable from 'nomad-ui/mixins/searchable'; import { lazyClick } from 'nomad-ui/helpers/lazy-click'; +import classic from 'ember-classic-decorator'; -export default Controller.extend( - SortableFactory([ - 'plainId', - 'controllersHealthyProportion', - 'nodesHealthyProportion', - 'provider', - ]), - Searchable, - { - userSettings: service(), - pluginsController: controller('csi/plugins'), +@classic +export default class IndexController extends Controller.extend( + SortableFactory([ + 'plainId', + 'controllersHealthyProportion', + 'nodesHealthyProportion', + 'provider', + ]), + Searchable + ) { + @service userSettings; + @controller('csi/plugins') pluginsController; - isForbidden: alias('pluginsController.isForbidden'), + @alias('pluginsController.isForbidden') isForbidden; - queryParams: { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - }, + queryParams = { + currentPage: 'page', + searchTerm: 'search', + sortProperty: 'sort', + sortDescending: 'desc', + }; - currentPage: 1, - pageSize: readOnly('userSettings.pageSize'), + currentPage = 1; + @readOnly('userSettings.pageSize') pageSize; - searchProps: computed(function() { - return ['id']; - }), - fuzzySearchProps: computed(function() { - return ['id']; - }), - - sortProperty: 'id', - sortDescending: false, - - listToSort: alias('model'), - listToSearch: alias('listSorted'), - sortedPlugins: alias('listSearched'), - - actions: { - gotoPlugin(plugin, event) { - lazyClick([() => this.transitionToRoute('csi.plugins.plugin', plugin.plainId), event]); - }, - }, + @computed + get searchProps() { + return ['id']; } -); + + @computed + get fuzzySearchProps() { + return ['id']; + } + + sortProperty = 'id'; + sortDescending = false; + + @alias('model') listToSort; + @alias('listSorted') listToSearch; + @alias('listSearched') sortedPlugins; + + @action + gotoPlugin(plugin, event) { + lazyClick([() => this.transitionToRoute('csi.plugins.plugin', plugin.plainId), event]); + } +} diff --git a/ui/app/controllers/csi/plugins/plugin/allocations.js b/ui/app/controllers/csi/plugins/plugin/allocations.js index 941235693..35a250a34 100644 --- a/ui/app/controllers/csi/plugins/plugin/allocations.js +++ b/ui/app/controllers/csi/plugins/plugin/allocations.js @@ -1,86 +1,92 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import { alias, readOnly } from '@ember/object/computed'; import SortableFactory from 'nomad-ui/mixins/sortable-factory'; import { lazyClick } from 'nomad-ui/helpers/lazy-click'; import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(SortableFactory(['updateTime', 'healthy']), { - userSettings: service(), +@classic +export default class AllocationsController extends Controller.extend( + SortableFactory(['updateTime', 'healthy']) + ) { + @service userSettings; - queryParams: { + queryParams = { currentPage: 'page', sortProperty: 'sort', sortDescending: 'desc', qpHealth: 'healthy', qpType: 'type', - }, + }; - currentPage: 1, - pageSize: readOnly('userSettings.pageSize'), + currentPage = 1; + @readOnly('userSettings.pageSize') pageSize; - sortProperty: 'updateTime', - sortDescending: false, + sortProperty = 'updateTime'; + sortDescending = false; - qpType: '', - qpHealth: '', + qpType = ''; + qpHealth = ''; - selectionType: selection('qpType'), - selectionHealth: selection('qpHealth'), + @selection('qpType') selectionType; + @selection('qpHealth') selectionHealth; - optionsType: computed(function() { + @computed + get optionsType() { return [{ key: 'controller', label: 'Controller' }, { key: 'node', label: 'Node' }]; - }), + } - optionsHealth: computed(function() { + @computed + get optionsHealth() { return [{ key: 'true', label: 'Healthy' }, { key: 'false', label: 'Unhealthy' }]; - }), + } - combinedAllocations: computed('model.{controllers.[],nodes.[]}', function() { + @computed('model.{controllers.[],nodes.[]}') + get combinedAllocations() { return this.model.controllers.toArray().concat(this.model.nodes.toArray()); - }), + } - filteredAllocations: computed( + @computed( 'combinedAllocations.[]', 'model.{controllers.[],nodes.[]}', 'selectionType', - 'selectionHealth', - function() { - const { selectionType: types, selectionHealth: healths } = this; + 'selectionHealth' + ) + get filteredAllocations() { + const { selectionType: types, selectionHealth: healths } = this; - // Instead of filtering the combined list, revert back to one of the two - // pre-existing lists. - let listToFilter = this.combinedAllocations; - if (types.length === 1 && types[0] === 'controller') { - listToFilter = this.model.controllers; - } else if (types.length === 1 && types[0] === 'node') { - listToFilter = this.model.nodes; - } - - if (healths.length === 1 && healths[0] === 'true') return listToFilter.filterBy('healthy'); - if (healths.length === 1 && healths[0] === 'false') - return listToFilter.filterBy('healthy', false); - return listToFilter; + // Instead of filtering the combined list, revert back to one of the two + // pre-existing lists. + let listToFilter = this.combinedAllocations; + if (types.length === 1 && types[0] === 'controller') { + listToFilter = this.model.controllers; + } else if (types.length === 1 && types[0] === 'node') { + listToFilter = this.model.nodes; } - ), - listToSort: alias('filteredAllocations'), - sortedAllocations: alias('listSorted'), + if (healths.length === 1 && healths[0] === 'true') return listToFilter.filterBy('healthy'); + if (healths.length === 1 && healths[0] === 'false') + return listToFilter.filterBy('healthy', false); + return listToFilter; + } + + @alias('filteredAllocations') listToSort; + @alias('listSorted') sortedAllocations; resetPagination() { if (this.currentPage != null) { this.set('currentPage', 1); } - }, + } setFacetQueryParam(queryParam, selection) { this.set(queryParam, serialize(selection)); - }, + } - actions: { - gotoAllocation(allocation, event) { - lazyClick([() => this.transitionToRoute('allocations.allocation', allocation), event]); - }, - }, -}); + @action + gotoAllocation(allocation, event) { + lazyClick([() => this.transitionToRoute('allocations.allocation', allocation), event]); + } +} diff --git a/ui/app/controllers/csi/plugins/plugin/index.js b/ui/app/controllers/csi/plugins/plugin/index.js index fcd6977e6..863eaff63 100644 --- a/ui/app/controllers/csi/plugins/plugin/index.js +++ b/ui/app/controllers/csi/plugins/plugin/index.js @@ -1,18 +1,19 @@ import Controller from '@ember/controller'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; -export default Controller.extend({ - sortedControllers: computed('model.controllers.@each.updateTime', function() { +export default class IndexController extends Controller { + @computed('model.controllers.@each.updateTime') + get sortedControllers() { return this.model.controllers.sortBy('updateTime').reverse(); - }), + } - sortedNodes: computed('model.nodes.@each.updateTime', function() { + @computed('model.nodes.@each.updateTime') + get sortedNodes() { return this.model.nodes.sortBy('updateTime').reverse(); - }), + } - actions: { - gotoAllocation(allocation) { - this.transitionToRoute('allocations.allocation', allocation); - }, - }, -}); + @action + gotoAllocation(allocation) { + this.transitionToRoute('allocations.allocation', allocation); + } +} diff --git a/ui/app/controllers/csi/volumes.js b/ui/app/controllers/csi/volumes.js index bd4bec291..1a48a6208 100644 --- a/ui/app/controllers/csi/volumes.js +++ b/ui/app/controllers/csi/volumes.js @@ -1,11 +1,11 @@ import Controller from '@ember/controller'; -export default Controller.extend({ - queryParams: { +export default class VolumesController extends Controller { + queryParams = { volumeNamespace: 'namespace', - }, + }; - isForbidden: false, + isForbidden = false; - volumeNamespace: 'default', -}); + volumeNamespace = 'default'; +} diff --git a/ui/app/controllers/csi/volumes/index.js b/ui/app/controllers/csi/volumes/index.js index 37a17234a..8f7abb9be 100644 --- a/ui/app/controllers/csi/volumes/index.js +++ b/ui/app/controllers/csi/volumes/index.js @@ -1,75 +1,78 @@ import { inject as service } from '@ember/service'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import { alias, readOnly } from '@ember/object/computed'; import Controller, { inject as controller } from '@ember/controller'; import SortableFactory from 'nomad-ui/mixins/sortable-factory'; import Searchable from 'nomad-ui/mixins/searchable'; import { lazyClick } from 'nomad-ui/helpers/lazy-click'; +import classic from 'ember-classic-decorator'; -export default Controller.extend( - SortableFactory([ - 'id', - 'schedulable', - 'controllersHealthyProportion', - 'nodesHealthyProportion', - 'provider', - ]), - Searchable, - { - system: service(), - userSettings: service(), - volumesController: controller('csi/volumes'), +@classic +export default class IndexController extends Controller.extend( + SortableFactory([ + 'id', + 'schedulable', + 'controllersHealthyProportion', + 'nodesHealthyProportion', + 'provider', + ]), + Searchable + ) { + @service system; + @service userSettings; + @controller('csi/volumes') volumesController; - isForbidden: alias('volumesController.isForbidden'), + @alias('volumesController.isForbidden') + isForbidden; - queryParams: { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - }, + queryParams = { + currentPage: 'page', + searchTerm: 'search', + sortProperty: 'sort', + sortDescending: 'desc', + }; - currentPage: 1, - pageSize: readOnly('userSettings.pageSize'), + currentPage = 1; + @readOnly('userSettings.pageSize') pageSize; - sortProperty: 'id', - sortDescending: false, + sortProperty = 'id'; + sortDescending = false; - searchProps: computed(function() { - return ['name']; - }), - fuzzySearchProps: computed(function() { - return ['name']; - }), - fuzzySearchEnabled: true, - - /** - Visible volumes are those that match the selected namespace - */ - visibleVolumes: computed('model.{[],@each.parent}', function() { - if (!this.model) return []; - - // Namespace related properties are ommitted from the dependent keys - // due to a prop invalidation bug caused by region switching. - const hasNamespaces = this.get('system.namespaces.length'); - const activeNamespace = this.get('system.activeNamespace.id') || 'default'; - - return this.model - .compact() - .filter(volume => !hasNamespaces || volume.get('namespace.id') === activeNamespace); - }), - - listToSort: alias('visibleVolumes'), - listToSearch: alias('listSorted'), - sortedVolumes: alias('listSearched'), - - actions: { - gotoVolume(volume, event) { - lazyClick([ - () => this.transitionToRoute('csi.volumes.volume', volume.get('plainId')), - event, - ]); - }, - }, + @computed + get searchProps() { + return ['name']; } -); + + @computed + get fuzzySearchProps() { + return ['name']; + } + + fuzzySearchEnabled = true; + + /** + Visible volumes are those that match the selected namespace + */ + @computed('model.{[],@each.parent}') + get visibleVolumes() { + if (!this.model) return []; + + // Namespace related properties are ommitted from the dependent keys + // due to a prop invalidation bug caused by region switching. + const hasNamespaces = this.get('system.namespaces.length'); + const activeNamespace = this.get('system.activeNamespace.id') || 'default'; + + return this.model + .compact() + .filter(volume => !hasNamespaces || volume.get('namespace.id') === activeNamespace); + } + + @alias('visibleVolumes') listToSort; + @alias('listSorted') listToSearch; + @alias('listSearched') sortedVolumes; + + @action + gotoVolume(volume, event) { + lazyClick([() => this.transitionToRoute('csi.volumes.volume', volume.get('plainId')), event]); + } +} diff --git a/ui/app/controllers/csi/volumes/volume.js b/ui/app/controllers/csi/volumes/volume.js index 5e43260b4..d0f6ce12d 100644 --- a/ui/app/controllers/csi/volumes/volume.js +++ b/ui/app/controllers/csi/volumes/volume.js @@ -1,22 +1,23 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; -export default Controller.extend({ +export default class VolumeController extends Controller { // Used in the template - system: service(), + @service system; - sortedReadAllocations: computed('model.readAllocations.@each.modifyIndex', function() { + @computed('model.readAllocations.@each.modifyIndex') + get sortedReadAllocations() { return this.model.readAllocations.sortBy('modifyIndex').reverse(); - }), + } - sortedWriteAllocations: computed('model.writeAllocations.@each.modifyIndex', function() { + @computed('model.writeAllocations.@each.modifyIndex') + get sortedWriteAllocations() { return this.model.writeAllocations.sortBy('modifyIndex').reverse(); - }), + } - actions: { - gotoAllocation(allocation) { - this.transitionToRoute('allocations.allocation', allocation); - }, - }, -}); + @action + gotoAllocation(allocation) { + this.transitionToRoute('allocations.allocation', allocation); + } +} diff --git a/ui/app/controllers/jobs.js b/ui/app/controllers/jobs.js index b38d2b11b..a077a5705 100644 --- a/ui/app/controllers/jobs.js +++ b/ui/app/controllers/jobs.js @@ -1,14 +1,14 @@ import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; -export default Controller.extend({ - system: service(), +export default class JobsController extends Controller { + @service system; - queryParams: { + queryParams = { jobNamespace: 'namespace', - }, + }; - isForbidden: false, + isForbidden = false; - jobNamespace: 'default', -}); + jobNamespace = 'default'; +} diff --git a/ui/app/controllers/jobs/index.js b/ui/app/controllers/jobs/index.js index a3c1bbad7..1b2f2713c 100644 --- a/ui/app/controllers/jobs/index.js +++ b/ui/app/controllers/jobs/index.js @@ -2,21 +2,23 @@ import { inject as service } from '@ember/service'; import { alias, readOnly } from '@ember/object/computed'; import Controller, { inject as controller } from '@ember/controller'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import { scheduleOnce } from '@ember/runloop'; import intersection from 'lodash.intersection'; import Sortable from 'nomad-ui/mixins/sortable'; import Searchable from 'nomad-ui/mixins/searchable'; import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(Sortable, Searchable, { - system: service(), - userSettings: service(), - jobsController: controller('jobs'), +@classic +export default class IndexController extends Controller.extend(Sortable, Searchable) { + @service system; + @service userSettings; + @controller('jobs') jobsController; - isForbidden: alias('jobsController.isForbidden'), + @alias('jobsController.isForbidden') isForbidden; - queryParams: { + queryParams = { currentPage: 'page', searchTerm: 'search', sortProperty: 'sort', @@ -25,33 +27,38 @@ export default Controller.extend(Sortable, Searchable, { qpStatus: 'status', qpDatacenter: 'dc', qpPrefix: 'prefix', - }, + }; - currentPage: 1, - pageSize: readOnly('userSettings.pageSize'), + currentPage = 1; + @readOnly('userSettings.pageSize') pageSize; - sortProperty: 'modifyIndex', - sortDescending: true, + sortProperty = 'modifyIndex'; + sortDescending = true; - searchProps: computed(function() { + @computed + get searchProps() { return ['id', 'name']; - }), - fuzzySearchProps: computed(function() { + } + + @computed + get fuzzySearchProps() { return ['name']; - }), - fuzzySearchEnabled: true, + } - qpType: '', - qpStatus: '', - qpDatacenter: '', - qpPrefix: '', + fuzzySearchEnabled = true; - selectionType: selection('qpType'), - selectionStatus: selection('qpStatus'), - selectionDatacenter: selection('qpDatacenter'), - selectionPrefix: selection('qpPrefix'), + qpType = ''; + qpStatus = ''; + qpDatacenter = ''; + qpPrefix = ''; - optionsType: computed(function() { + @selection('qpType') selectionType; + @selection('qpStatus') selectionStatus; + @selection('qpDatacenter') selectionDatacenter; + @selection('qpPrefix') selectionPrefix; + + @computed + get optionsType() { return [ { key: 'batch', label: 'Batch' }, { key: 'parameterized', label: 'Parameterized' }, @@ -59,17 +66,19 @@ export default Controller.extend(Sortable, Searchable, { { key: 'service', label: 'Service' }, { key: 'system', label: 'System' }, ]; - }), + } - optionsStatus: computed(function() { + @computed + get optionsStatus() { return [ { key: 'pending', label: 'Pending' }, { key: 'running', label: 'Running' }, { key: 'dead', label: 'Dead' }, ]; - }), + } - optionsDatacenter: computed('visibleJobs.[]', function() { + @computed('visibleJobs.[]') + get optionsDatacenter() { const flatten = (acc, val) => acc.concat(val); const allDatacenters = new Set(this.visibleJobs.mapBy('datacenters').reduce(flatten, [])); @@ -84,9 +93,10 @@ export default Controller.extend(Sortable, Searchable, { }); return availableDatacenters.sort().map(dc => ({ key: dc, label: dc })); - }), + } - optionsPrefix: computed('visibleJobs.[]', function() { + @computed('visibleJobs.[]') + get optionsPrefix() { // A prefix is defined as the start of a job name up to the first - or . // ex: mktg-analytics -> mktg, ds.supermodel.classifier -> ds const hasPrefix = /.[-._]/; @@ -122,13 +132,14 @@ export default Controller.extend(Sortable, Searchable, { key: name.prefix, label: `${name.prefix} (${name.count})`, })); - }), + } /** Visible jobs are those that match the selected namespace and aren't children of periodic or parameterized jobs. */ - visibleJobs: computed('model.{[],@each.parent}', function() { + @computed('model.{[],@each.parent}') + get visibleJobs() { // Namespace related properties are ommitted from the dependent keys // due to a prop invalidation bug caused by region switching. const hasNamespaces = this.get('system.namespaces.length'); @@ -138,60 +149,59 @@ export default Controller.extend(Sortable, Searchable, { .compact() .filter(job => !hasNamespaces || job.get('namespace.id') === activeNamespace) .filter(job => !job.get('parent.content')); - }), + } - filteredJobs: computed( + @computed( 'visibleJobs.[]', 'selectionType', 'selectionStatus', 'selectionDatacenter', - 'selectionPrefix', - function() { - const { - selectionType: types, - selectionStatus: statuses, - selectionDatacenter: datacenters, - selectionPrefix: prefixes, - } = this; + 'selectionPrefix' + ) + get filteredJobs() { + const { + selectionType: types, + selectionStatus: statuses, + selectionDatacenter: datacenters, + selectionPrefix: prefixes, + } = this; - // A job must match ALL filter facets, but it can match ANY selection within a facet - // Always return early to prevent unnecessary facet predicates. - return this.visibleJobs.filter(job => { - if (types.length && !types.includes(job.get('displayType'))) { - return false; - } + // A job must match ALL filter facets, but it can match ANY selection within a facet + // Always return early to prevent unnecessary facet predicates. + return this.visibleJobs.filter(job => { + if (types.length && !types.includes(job.get('displayType'))) { + return false; + } - if (statuses.length && !statuses.includes(job.get('status'))) { - return false; - } + if (statuses.length && !statuses.includes(job.get('status'))) { + return false; + } - if (datacenters.length && !job.get('datacenters').find(dc => datacenters.includes(dc))) { - return false; - } + if (datacenters.length && !job.get('datacenters').find(dc => datacenters.includes(dc))) { + return false; + } - const name = job.get('name'); - if (prefixes.length && !prefixes.find(prefix => name.startsWith(prefix))) { - return false; - } + const name = job.get('name'); + if (prefixes.length && !prefixes.find(prefix => name.startsWith(prefix))) { + return false; + } - return true; - }); - } - ), + return true; + }); + } - listToSort: alias('filteredJobs'), - listToSearch: alias('listSorted'), - sortedJobs: alias('listSearched'), + @alias('filteredJobs') listToSort; + @alias('listSorted') listToSearch; + @alias('listSearched') sortedJobs; - isShowingDeploymentDetails: false, + isShowingDeploymentDetails = false; setFacetQueryParam(queryParam, selection) { this.set(queryParam, serialize(selection)); - }, + } - actions: { - gotoJob(job) { - this.transitionToRoute('jobs.job', job.get('plainId')); - }, - }, -}); + @action + gotoJob(job) { + this.transitionToRoute('jobs.job', job.get('plainId')); + } +} diff --git a/ui/app/controllers/jobs/job/allocations.js b/ui/app/controllers/jobs/job/allocations.js index 0de958725..f4688b3af 100644 --- a/ui/app/controllers/jobs/job/allocations.js +++ b/ui/app/controllers/jobs/job/allocations.js @@ -1,41 +1,48 @@ import { alias } from '@ember/object/computed'; import Controller from '@ember/controller'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import Sortable from 'nomad-ui/mixins/sortable'; import Searchable from 'nomad-ui/mixins/searchable'; import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(Sortable, Searchable, WithNamespaceResetting, { - queryParams: { +@classic +export default class AllocationsController extends Controller.extend( + Sortable, + Searchable, + WithNamespaceResetting + ) { + queryParams = { currentPage: 'page', searchTerm: 'search', sortProperty: 'sort', sortDescending: 'desc', - }, + }; - currentPage: 1, - pageSize: 25, + currentPage = 1; + pageSize = 25; - sortProperty: 'modifyIndex', - sortDescending: true, + sortProperty = 'modifyIndex'; + sortDescending = true; - job: alias('model'), + @alias('model') job; - searchProps: computed(function() { + @computed + get searchProps() { return ['shortId', 'name', 'taskGroupName']; - }), + } - allocations: computed('model.allocations.[]', function() { + @computed('model.allocations.[]') + get allocations() { return this.get('model.allocations') || []; - }), + } - listToSort: alias('allocations'), - listToSearch: alias('listSorted'), - sortedAllocations: alias('listSearched'), + @alias('allocations') listToSort; + @alias('listSorted') listToSearch; + @alias('listSearched') sortedAllocations; - actions: { - gotoAllocation(allocation) { - this.transitionToRoute('allocations.allocation', allocation); - }, - }, -}); + @action + gotoAllocation(allocation) { + this.transitionToRoute('allocations.allocation', allocation); + } +} diff --git a/ui/app/controllers/jobs/job/definition.js b/ui/app/controllers/jobs/job/definition.js index 910255c2d..16c3390f5 100644 --- a/ui/app/controllers/jobs/job/definition.js +++ b/ui/app/controllers/jobs/job/definition.js @@ -1,25 +1,27 @@ import Controller from '@ember/controller'; import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; import { alias } from '@ember/object/computed'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(WithNamespaceResetting, { - job: alias('model.job'), - definition: alias('model.definition'), +@classic +export default class DefinitionController extends Controller.extend(WithNamespaceResetting) { + @alias('model.job') job; + @alias('model.definition') definition; - isEditing: false, + isEditing = false; edit() { this.job.set('_newDefinition', JSON.stringify(this.definition, null, 2)); this.set('isEditing', true); - }, + } onCancel() { this.set('isEditing', false); - }, + } onSubmit(id, namespace) { this.transitionToRoute('jobs.job', id, { queryParams: { jobNamespace: namespace }, }); - }, -}); + } +} diff --git a/ui/app/controllers/jobs/job/deployments.js b/ui/app/controllers/jobs/job/deployments.js index 1c50d92e9..cbce847e2 100644 --- a/ui/app/controllers/jobs/job/deployments.js +++ b/ui/app/controllers/jobs/job/deployments.js @@ -1,7 +1,9 @@ import Controller from '@ember/controller'; import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; import { alias } from '@ember/object/computed'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(WithNamespaceResetting, { - job: alias('model'), -}); +@classic +export default class DeploymentsController extends Controller.extend(WithNamespaceResetting) { + @alias('model') job; +} diff --git a/ui/app/controllers/jobs/job/evaluations.js b/ui/app/controllers/jobs/job/evaluations.js index a6da8940f..b88ee42e0 100644 --- a/ui/app/controllers/jobs/job/evaluations.js +++ b/ui/app/controllers/jobs/job/evaluations.js @@ -2,19 +2,24 @@ import { alias } from '@ember/object/computed'; import Controller from '@ember/controller'; import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; import Sortable from 'nomad-ui/mixins/sortable'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(WithNamespaceResetting, Sortable, { - queryParams: { +@classic +export default class EvaluationsController extends Controller.extend( + WithNamespaceResetting, + Sortable + ) { + queryParams = { sortProperty: 'sort', sortDescending: 'desc', - }, + }; - sortProperty: 'modifyIndex', - sortDescending: true, + sortProperty = 'modifyIndex'; + sortDescending = true; - job: alias('model'), - evaluations: alias('model.evaluations'), + @alias('model') job; + @alias('model.evaluations') evaluations; - listToSort: alias('evaluations'), - sortedEvaluations: alias('listSorted'), -}); + @alias('evaluations') listToSort; + @alias('listSorted') sortedEvaluations; +} diff --git a/ui/app/controllers/jobs/job/index.js b/ui/app/controllers/jobs/job/index.js index 20fc91127..89ef1850d 100644 --- a/ui/app/controllers/jobs/job/index.js +++ b/ui/app/controllers/jobs/job/index.js @@ -2,32 +2,35 @@ import { inject as service } from '@ember/service'; import { alias } from '@ember/object/computed'; import Controller from '@ember/controller'; import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; +import { action } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(WithNamespaceResetting, { - system: service(), +@classic +export default class IndexController extends Controller.extend(WithNamespaceResetting) { + @service system; - queryParams: { + queryParams = { currentPage: 'page', sortProperty: 'sort', sortDescending: 'desc', - }, + }; - currentPage: 1, + currentPage = 1; - job: alias('model'), + @alias('model') job; - sortProperty: 'name', - sortDescending: false, + sortProperty = 'name'; + sortDescending = false; - actions: { - gotoTaskGroup(taskGroup) { - this.transitionToRoute('jobs.job.task-group', taskGroup.get('job'), taskGroup); - }, + @action + gotoTaskGroup(taskGroup) { + this.transitionToRoute('jobs.job.task-group', taskGroup.get('job'), taskGroup); + } - gotoJob(job) { - this.transitionToRoute('jobs.job', job, { - queryParams: { jobNamespace: job.get('namespace.name') }, - }); - }, - }, -}); + @action + gotoJob(job) { + this.transitionToRoute('jobs.job', job, { + queryParams: { jobNamespace: job.get('namespace.name') }, + }); + } +} diff --git a/ui/app/controllers/jobs/job/task-group.js b/ui/app/controllers/jobs/job/task-group.js index 95127c4ed..365ec12ae 100644 --- a/ui/app/controllers/jobs/job/task-group.js +++ b/ui/app/controllers/jobs/job/task-group.js @@ -1,42 +1,49 @@ -import { alias, readOnly } from '@ember/object/computed'; import { inject as service } from '@ember/service'; +import { alias, readOnly } from '@ember/object/computed'; import Controller from '@ember/controller'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import Sortable from 'nomad-ui/mixins/sortable'; import Searchable from 'nomad-ui/mixins/searchable'; import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(Sortable, Searchable, WithNamespaceResetting, { - userSettings: service(), +@classic +export default class TaskGroupController extends Controller.extend( + Sortable, + Searchable, + WithNamespaceResetting + ) { + @service userSettings; - queryParams: { + queryParams = { currentPage: 'page', searchTerm: 'search', sortProperty: 'sort', sortDescending: 'desc', - }, + }; - currentPage: 1, - pageSize: readOnly('userSettings.pageSize'), + currentPage = 1; + @readOnly('userSettings.pageSize') pageSize; - sortProperty: 'modifyIndex', - sortDescending: true, + sortProperty = 'modifyIndex'; + sortDescending = true; - searchProps: computed(function() { + @computed + get searchProps() { return ['shortId', 'name']; - }), + } - allocations: computed('model.allocations.[]', function() { + @computed('model.allocations.[]') + get allocations() { return this.get('model.allocations') || []; - }), + } - listToSort: alias('allocations'), - listToSearch: alias('listSorted'), - sortedAllocations: alias('listSearched'), + @alias('allocations') listToSort; + @alias('listSorted') listToSearch; + @alias('listSearched') sortedAllocations; - actions: { - gotoAllocation(allocation) { - this.transitionToRoute('allocations.allocation', allocation); - }, - }, -}); + @action + gotoAllocation(allocation) { + this.transitionToRoute('allocations.allocation', allocation); + } +} diff --git a/ui/app/controllers/jobs/job/versions.js b/ui/app/controllers/jobs/job/versions.js index 1c50d92e9..ca1989177 100644 --- a/ui/app/controllers/jobs/job/versions.js +++ b/ui/app/controllers/jobs/job/versions.js @@ -1,7 +1,9 @@ import Controller from '@ember/controller'; import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; import { alias } from '@ember/object/computed'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(WithNamespaceResetting, { - job: alias('model'), -}); +@classic +export default class VersionsController extends Controller.extend(WithNamespaceResetting) { + @alias('model') job; +} diff --git a/ui/app/controllers/jobs/run.js b/ui/app/controllers/jobs/run.js index baaf84183..f57d43023 100644 --- a/ui/app/controllers/jobs/run.js +++ b/ui/app/controllers/jobs/run.js @@ -1,9 +1,9 @@ import Controller from '@ember/controller'; -export default Controller.extend({ +export default class RunController extends Controller { onSubmit(id, namespace) { this.transitionToRoute('jobs.job', id, { queryParams: { jobNamespace: namespace }, }); - }, -}); + } +} diff --git a/ui/app/controllers/servers.js b/ui/app/controllers/servers.js index 5e6f1bea4..c3374ca5d 100644 --- a/ui/app/controllers/servers.js +++ b/ui/app/controllers/servers.js @@ -2,24 +2,24 @@ import { alias } from '@ember/object/computed'; import Controller from '@ember/controller'; import Sortable from 'nomad-ui/mixins/sortable'; -export default Controller.extend(Sortable, { - nodes: alias('model.nodes'), - agents: alias('model.agents'), +export default class ServersController extends Controller.extend(Sortable) { + @alias('model.nodes') nodes; + @alias('model.agents') agents; - queryParams: { + queryParams = { currentPage: 'page', sortProperty: 'sort', sortDescending: 'desc', - }, + }; - currentPage: 1, - pageSize: 8, + currentPage = 1; + pageSize = 8; - sortProperty: 'isLeader', - sortDescending: true, + sortProperty = 'isLeader'; + sortDescending = true; - isForbidden: false, + isForbidden = false; - listToSort: alias('agents'), - sortedAgents: alias('listSorted'), -}); + @alias('agents') listToSort; + @alias('listSorted') sortedAgents; +} diff --git a/ui/app/controllers/servers/index.js b/ui/app/controllers/servers/index.js index ee49e8f72..67d427df7 100644 --- a/ui/app/controllers/servers/index.js +++ b/ui/app/controllers/servers/index.js @@ -1,7 +1,7 @@ import { alias } from '@ember/object/computed'; import Controller, { inject as controller } from '@ember/controller'; -export default Controller.extend({ - serversController: controller('servers'), - isForbidden: alias('serversController.isForbidden'), -}); +export default class IndexController extends Controller { + @controller('servers') serversController; + @alias('serversController.isForbidden') isForbidden; +} diff --git a/ui/app/controllers/servers/server.js b/ui/app/controllers/servers/server.js index dd455b3ad..1b511c22f 100644 --- a/ui/app/controllers/servers/server.js +++ b/ui/app/controllers/servers/server.js @@ -1,10 +1,13 @@ import Controller from '@ember/controller'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default Controller.extend({ - activeTab: 'tags', +@classic +export default class ServerController extends Controller { + activeTab = 'tags'; - sortedTags: computed('model.tags', function() { + @computed('model.tags') + get sortedTags() { const tags = this.get('model.tags') || {}; return Object.keys(tags) .map(name => ({ @@ -12,11 +15,10 @@ export default Controller.extend({ value: tags[name], })) .sortBy('name'); - }), + } - actions: { - setTab(tab) { - this.set('activeTab', tab); - }, - }, -}); + @action + setTab(tab) { + this.set('activeTab', tab); + } +} diff --git a/ui/app/models/agent.js b/ui/app/models/agent.js index 0c210e69e..7eefb3c6b 100644 --- a/ui/app/models/agent.js +++ b/ui/app/models/agent.js @@ -2,25 +2,29 @@ import { inject as service } from '@ember/service'; import { computed } from '@ember/object'; import Model from 'ember-data/model'; import attr from 'ember-data/attr'; +import classic from 'ember-classic-decorator'; -export default Model.extend({ - system: service(), +@classic +export default class Agent extends Model { + @service system; - name: attr('string'), - address: attr('string'), - serfPort: attr('string'), - rpcPort: attr('string'), - tags: attr({ defaultValue: () => ({}) }), - status: attr('string'), - datacenter: attr('string'), - region: attr('string'), + @attr('string') name; + @attr('string') address; + @attr('string') serfPort; + @attr('string') rpcPort; + @attr({ defaultValue: () => ({}) }) tags; + @attr('string') status; + @attr('string') datacenter; + @attr('string') region; - rpcAddr: computed('address', 'port', function() { + @computed('address', 'port') + get rpcAddr() { const { address, rpcPort } = this; return address && rpcPort && `${address}:${rpcPort}`; - }), + } - isLeader: computed('system.leader.rpcAddr', function() { + @computed('system.leader.rpcAddr') + get isLeader() { return this.get('system.leader.rpcAddr') === this.rpcAddr; - }), -}); + } +} diff --git a/ui/app/models/allocation.js b/ui/app/models/allocation.js index 44a852428..a9a60d351 100644 --- a/ui/app/models/allocation.js +++ b/ui/app/models/allocation.js @@ -7,6 +7,7 @@ import { belongsTo, hasMany } from 'ember-data/relationships'; import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes'; import intersection from 'lodash.intersection'; import shortUUIDProperty from '../utils/properties/short-uuid'; +import classic from 'ember-classic-decorator'; const STATUS_ORDER = { pending: 1, @@ -16,50 +17,54 @@ const STATUS_ORDER = { lost: 5, }; -export default Model.extend({ - token: service(), +@classic +export default class Allocation extends Model { + @service token; - shortId: shortUUIDProperty('id'), - job: belongsTo('job'), - node: belongsTo('node'), - name: attr('string'), - taskGroupName: attr('string'), - resources: fragment('resources'), - allocatedResources: fragment('resources'), - jobVersion: attr('number'), + @shortUUIDProperty('id') shortId; + @belongsTo('job') job; + @belongsTo('node') node; + @attr('string') name; + @attr('string') taskGroupName; + @fragment('resources') resources; + @fragment('resources') allocatedResources; + @attr('number') jobVersion; - modifyIndex: attr('number'), - modifyTime: attr('date'), + @attr('number') modifyIndex; + @attr('date') modifyTime; - createIndex: attr('number'), - createTime: attr('date'), + @attr('number') createIndex; + @attr('date') createTime; - clientStatus: attr('string'), - desiredStatus: attr('string'), - statusIndex: computed('clientStatus', function() { + @attr('string') clientStatus; + @attr('string') desiredStatus; + + @computed('clientStatus') + get statusIndex() { return STATUS_ORDER[this.clientStatus] || 100; - }), + } - isRunning: equal('clientStatus', 'running'), - isMigrating: attr('boolean'), + @equal('clientStatus', 'running') isRunning; + @attr('boolean') isMigrating; // An allocation model created from any allocation list response will be lacking // many properties (some of which can always be null). This is an indicator that // the allocation needs to be reloaded to get the complete allocation state. - isPartial: none('allocationTaskGroup'), + @none('allocationTaskGroup') isPartial; // When allocations are server-side rescheduled, a paper trail // is left linking all reschedule attempts. - previousAllocation: belongsTo('allocation', { inverse: 'nextAllocation' }), - nextAllocation: belongsTo('allocation', { inverse: 'previousAllocation' }), + @belongsTo('allocation', { inverse: 'nextAllocation' }) previousAllocation; + @belongsTo('allocation', { inverse: 'previousAllocation' }) nextAllocation; - preemptedAllocations: hasMany('allocation', { inverse: 'preemptedByAllocation' }), - preemptedByAllocation: belongsTo('allocation', { inverse: 'preemptedAllocations' }), - wasPreempted: attr('boolean'), + @hasMany('allocation', { inverse: 'preemptedByAllocation' }) preemptedAllocations; + @belongsTo('allocation', { inverse: 'preemptedAllocations' }) preemptedByAllocation; + @attr('boolean') wasPreempted; - followUpEvaluation: belongsTo('evaluation'), + @belongsTo('evaluation') followUpEvaluation; - statusClass: computed('clientStatus', function() { + @computed('clientStatus') + get statusClass() { const classMap = { pending: 'is-pending', running: 'is-primary', @@ -69,25 +74,29 @@ export default Model.extend({ }; return classMap[this.clientStatus] || 'is-dark'; - }), + } - isOld: computed('jobVersion', 'job.version', function() { + @computed('jobVersion', 'job.version') + get isOld() { return this.jobVersion !== this.get('job.version'); - }), + } - taskGroup: computed('isOld', 'jobTaskGroup', 'allocationTaskGroup', function() { + @computed('isOld', 'jobTaskGroup', 'allocationTaskGroup') + get taskGroup() { if (!this.isOld) return this.jobTaskGroup; return this.allocationTaskGroup; - }), + } - jobTaskGroup: computed('taskGroupName', 'job.taskGroups.[]', function() { + @computed('taskGroupName', 'job.taskGroups.[]') + get jobTaskGroup() { const taskGroups = this.get('job.taskGroups'); return taskGroups && taskGroups.findBy('name', this.taskGroupName); - }), + } - allocationTaskGroup: fragment('task-group', { defaultValue: null }), + @fragment('task-group', { defaultValue: null }) allocationTaskGroup; - unhealthyDrivers: computed('taskGroup.drivers.[]', 'node.unhealthyDriverNames.[]', function() { + @computed('taskGroup.drivers.[]', 'node.unhealthyDriverNames.[]') + get unhealthyDrivers() { const taskGroupUnhealthyDrivers = this.get('taskGroup.drivers'); const nodeUnhealthyDrivers = this.get('node.unhealthyDriverNames'); @@ -96,41 +105,38 @@ export default Model.extend({ } return []; - }), + } - states: fragmentArray('task-state'), - rescheduleEvents: fragmentArray('reschedule-event'), + @fragmentArray('task-state') states; + @fragmentArray('reschedule-event') rescheduleEvents; - hasRescheduleEvents: computed('rescheduleEvents.length', 'nextAllocation', function() { + @computed('rescheduleEvents.length', 'nextAllocation') + get hasRescheduleEvents() { return this.get('rescheduleEvents.length') > 0 || this.nextAllocation; - }), + } - hasStoppedRescheduling: computed( - 'nextAllocation', - 'clientStatus', - 'followUpEvaluation.content', - function() { - return ( - !this.get('nextAllocation.content') && - !this.get('followUpEvaluation.content') && - this.clientStatus === 'failed' - ); - } - ), + @computed('nextAllocation', 'clientStatus', 'followUpEvaluation.content') + get hasStoppedRescheduling() { + return ( + !this.get('nextAllocation.content') && + !this.get('followUpEvaluation.content') && + this.clientStatus === 'failed' + ); + } stop() { return this.store.adapterFor('allocation').stop(this); - }, + } restart(taskName) { return this.store.adapterFor('allocation').restart(this, taskName); - }, + } ls(path) { return this.store.adapterFor('allocation').ls(this, path); - }, + } stat(path) { return this.store.adapterFor('allocation').stat(this, path); - }, -}); + } +} diff --git a/ui/app/models/consul-connect.js b/ui/app/models/consul-connect.js index 3b617e126..168809401 100644 --- a/ui/app/models/consul-connect.js +++ b/ui/app/models/consul-connect.js @@ -1,6 +1,6 @@ import Fragment from 'ember-data-model-fragments/fragment'; import { fragment } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - sidecarService: fragment('sidecar-service'), -}); +export default class ConsulConnect extends Fragment { + @fragment('sidecar-service') sidecarService; +} diff --git a/ui/app/models/deployment.js b/ui/app/models/deployment.js index df7f767bd..66d6cb297 100644 --- a/ui/app/models/deployment.js +++ b/ui/app/models/deployment.js @@ -7,46 +7,53 @@ import { belongsTo, hasMany } from 'ember-data/relationships'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; import shortUUIDProperty from '../utils/properties/short-uuid'; import sumAggregation from '../utils/properties/sum-aggregation'; +import classic from 'ember-classic-decorator'; -export default Model.extend({ - shortId: shortUUIDProperty('id'), +@classic +export default class Deployment extends Model { + @shortUUIDProperty('id') shortId; - job: belongsTo('job', { inverse: 'deployments' }), - jobForLatest: belongsTo('job', { inverse: 'latestDeployment' }), - versionNumber: attr('number'), + @belongsTo('job', { inverse: 'deployments' }) job; + @belongsTo('job', { inverse: 'latestDeployment' }) jobForLatest; + @attr('number') versionNumber; // If any task group is not promoted yet requires promotion and the deployment // is still running, the deployment needs promotion. - requiresPromotion: computed('taskGroupSummaries.@each.promoted', function() { - return this.status === 'running' && - this.taskGroupSummaries - .toArray() - .some(summary => summary.get('requiresPromotion') && !summary.get('promoted')); - }), + @computed('taskGroupSummaries.@each.promoted') + get requiresPromotion() { + return ( + this.status === 'running' && + this.taskGroupSummaries + .toArray() + .some(summary => summary.get('requiresPromotion') && !summary.get('promoted')) + ); + } - status: attr('string'), - statusDescription: attr('string'), + @attr('string') status; + @attr('string') statusDescription; - isRunning: equal('status', 'running'), + @equal('status', 'running') isRunning; - taskGroupSummaries: fragmentArray('task-group-deployment-summary'), - allocations: hasMany('allocations'), + @fragmentArray('task-group-deployment-summary') taskGroupSummaries; + @hasMany('allocations') allocations; - version: computed('versionNumber', 'job.versions.content.@each.number', function() { + @computed('versionNumber', 'job.versions.content.@each.number') + get version() { return (this.get('job.versions') || []).findBy('number', this.versionNumber); - }), + } // Dependent keys can only go one level past an @each so an alias is needed - versionSubmitTime: alias('version.submitTime'), + @alias('version.submitTime') versionSubmitTime; - placedCanaries: sumAggregation('taskGroupSummaries', 'placedCanaries'), - desiredCanaries: sumAggregation('taskGroupSummaries', 'desiredCanaries'), - desiredTotal: sumAggregation('taskGroupSummaries', 'desiredTotal'), - placedAllocs: sumAggregation('taskGroupSummaries', 'placedAllocs'), - healthyAllocs: sumAggregation('taskGroupSummaries', 'healthyAllocs'), - unhealthyAllocs: sumAggregation('taskGroupSummaries', 'unhealthyAllocs'), + @sumAggregation('taskGroupSummaries', 'placedCanaries') placedCanaries; + @sumAggregation('taskGroupSummaries', 'desiredCanaries') desiredCanaries; + @sumAggregation('taskGroupSummaries', 'desiredTotal') desiredTotal; + @sumAggregation('taskGroupSummaries', 'placedAllocs') placedAllocs; + @sumAggregation('taskGroupSummaries', 'healthyAllocs') healthyAllocs; + @sumAggregation('taskGroupSummaries', 'unhealthyAllocs') unhealthyAllocs; - statusClass: computed('status', function() { + @computed('status') + get statusClass() { const classMap = { running: 'is-running', successful: 'is-primary', @@ -56,10 +63,10 @@ export default Model.extend({ }; return classMap[this.status] || 'is-dark'; - }), + } promote() { assert('A deployment needs to requirePromotion to be promoted', this.requiresPromotion); return this.store.adapterFor('deployment').promote(this); - }, -}); + } +} diff --git a/ui/app/models/drain-strategy.js b/ui/app/models/drain-strategy.js index 4d2738961..4b3aa67da 100644 --- a/ui/app/models/drain-strategy.js +++ b/ui/app/models/drain-strategy.js @@ -1,12 +1,14 @@ import { lt, equal } from '@ember/object/computed'; import attr from 'ember-data/attr'; import Fragment from 'ember-data-model-fragments/fragment'; +import classic from 'ember-classic-decorator'; -export default Fragment.extend({ - deadline: attr('number'), - forceDeadline: attr('date'), - ignoreSystemJobs: attr('boolean'), +@classic +export default class DrainStrategy extends Fragment { + @attr('number') deadline; + @attr('date') forceDeadline; + @attr('boolean') ignoreSystemJobs; - isForced: lt('deadline', 0), - hasNoDeadline: equal('deadline', 0), -}); + @lt('deadline', 0) isForced; + @equal('deadline', 0) hasNoDeadline; +} diff --git a/ui/app/models/evaluation.js b/ui/app/models/evaluation.js index 2b7b0f2e0..fb8fb98b3 100644 --- a/ui/app/models/evaluation.js +++ b/ui/app/models/evaluation.js @@ -5,25 +5,25 @@ import { belongsTo } from 'ember-data/relationships'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; import shortUUIDProperty from '../utils/properties/short-uuid'; -export default Model.extend({ - shortId: shortUUIDProperty('id'), - priority: attr('number'), - type: attr('string'), - triggeredBy: attr('string'), - status: attr('string'), - statusDescription: attr('string'), - failedTGAllocs: fragmentArray('placement-failure', { defaultValue: () => [] }), +export default class Evaluation extends Model { + @shortUUIDProperty('id') shortId; + @attr('number') priority; + @attr('string') type; + @attr('string') triggeredBy; + @attr('string') status; + @attr('string') statusDescription; + @fragmentArray('placement-failure', { defaultValue: () => [] }) failedTGAllocs; - hasPlacementFailures: bool('failedTGAllocs.length'), - isBlocked: equal('status', 'blocked'), + @bool('failedTGAllocs.length') hasPlacementFailures; + @equal('status', 'blocked') isBlocked; - job: belongsTo('job'), + @belongsTo('job') job; - modifyIndex: attr('number'), - modifyTime: attr('date'), + @attr('number') modifyIndex; + @attr('date') modifyTime; - createIndex: attr('number'), - createTime: attr('date'), + @attr('number') createIndex; + @attr('date') createTime; - waitUntil: attr('date'), -}); + @attr('date') waitUntil; +} diff --git a/ui/app/models/host-volume.js b/ui/app/models/host-volume.js index ff5d3e628..e6d3b7e31 100644 --- a/ui/app/models/host-volume.js +++ b/ui/app/models/host-volume.js @@ -2,10 +2,10 @@ import Fragment from 'ember-data-model-fragments/fragment'; import attr from 'ember-data/attr'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - node: fragmentOwner(), +export default class HostVolume extends Fragment { + @fragmentOwner() node; - name: attr('string'), - path: attr('string'), - readOnly: attr('boolean'), -}); + @attr('string') name; + @attr('string') path; + @attr('boolean') readOnly; +} diff --git a/ui/app/models/job-plan.js b/ui/app/models/job-plan.js index 1ddc3d4b8..714cf186c 100644 --- a/ui/app/models/job-plan.js +++ b/ui/app/models/job-plan.js @@ -3,8 +3,8 @@ import attr from 'ember-data/attr'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; import { hasMany } from 'ember-data/relationships'; -export default Model.extend({ - diff: attr(), - failedTGAllocs: fragmentArray('placement-failure', { defaultValue: () => [] }), - preemptions: hasMany('allocation'), -}); +export default class JobPlan extends Model { + @attr() diff; + @fragmentArray('placement-failure', { defaultValue: () => [] }) failedTGAllocs; + @hasMany('allocation') preemptions; +} diff --git a/ui/app/models/job-summary.js b/ui/app/models/job-summary.js index a9584aced..b32d09a52 100644 --- a/ui/app/models/job-summary.js +++ b/ui/app/models/job-summary.js @@ -4,36 +4,39 @@ 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'; +import classic from 'ember-classic-decorator'; -export default Model.extend({ - job: belongsTo('job'), +@classic +export default class JobSummary extends Model { + @belongsTo('job') job; - taskGroupSummaries: fragmentArray('task-group-summary'), + @fragmentArray('task-group-summary') taskGroupSummaries; // 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'), + @sumAggregation('taskGroupSummaries', 'queuedAllocs') queuedAllocs; + @sumAggregation('taskGroupSummaries', 'startingAllocs') startingAllocs; + @sumAggregation('taskGroupSummaries', 'runningAllocs') runningAllocs; + @sumAggregation('taskGroupSummaries', 'completeAllocs') completeAllocs; + @sumAggregation('taskGroupSummaries', 'failedAllocs') failedAllocs; + @sumAggregation('taskGroupSummaries', 'lostAllocs') lostAllocs; - allocsList: collect( + @collect( 'queuedAllocs', 'startingAllocs', 'runningAllocs', 'completeAllocs', 'failedAllocs', 'lostAllocs' - ), + ) + allocsList; - totalAllocs: sum('allocsList'), + @sum('allocsList') totalAllocs; - pendingChildren: attr('number'), - runningChildren: attr('number'), - deadChildren: attr('number'), + @attr('number') pendingChildren; + @attr('number') runningChildren; + @attr('number') deadChildren; - childrenList: collect('pendingChildren', 'runningChildren', 'deadChildren'), + @collect('pendingChildren', 'runningChildren', 'deadChildren') childrenList; - totalChildren: sum('childrenList'), -}); + @sum('childrenList') totalChildren; +} diff --git a/ui/app/models/job-version.js b/ui/app/models/job-version.js index 6204c1198..005f7657b 100644 --- a/ui/app/models/job-version.js +++ b/ui/app/models/job-version.js @@ -2,10 +2,10 @@ import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { belongsTo } from 'ember-data/relationships'; -export default Model.extend({ - job: belongsTo('job'), - stable: attr('boolean'), - submitTime: attr('date'), - number: attr('number'), - diff: attr(), -}); +export default class JobVersion extends Model { + @belongsTo('job') job; + @attr('boolean') stable; + @attr('date') submitTime; + @attr('number') number; + @attr() diff; +} diff --git a/ui/app/models/job.js b/ui/app/models/job.js index 5226b6bf9..9d5b495e6 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -6,121 +6,122 @@ import { belongsTo, hasMany } from 'ember-data/relationships'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; import RSVP from 'rsvp'; import { assert } from '@ember/debug'; +import classic from 'ember-classic-decorator'; const JOB_TYPES = ['service', 'batch', 'system']; -export default Model.extend({ - region: attr('string'), - name: attr('string'), - plainId: attr('string'), - type: attr('string'), - priority: attr('number'), - allAtOnce: attr('boolean'), +@classic +export default class Job extends Model { + @attr('string') region; + @attr('string') name; + @attr('string') plainId; + @attr('string') type; + @attr('number') priority; + @attr('boolean') allAtOnce; - status: attr('string'), - statusDescription: attr('string'), - createIndex: attr('number'), - modifyIndex: attr('number'), + @attr('string') status; + @attr('string') statusDescription; + @attr('number') createIndex; + @attr('number') modifyIndex; // True when the job is the parent periodic or parameterized jobs // Instances of periodic or parameterized jobs are false for both properties - periodic: attr('boolean'), - parameterized: attr('boolean'), - dispatched: attr('boolean'), + @attr('boolean') periodic; + @attr('boolean') parameterized; + @attr('boolean') dispatched; - periodicDetails: attr(), - parameterizedDetails: attr(), + @attr() periodicDetails; + @attr() parameterizedDetails; - hasChildren: computed('periodic', 'parameterized', 'dispatched', function() { + @computed('periodic', 'parameterized', 'dispatched') + get hasChildren() { return this.periodic || (this.parameterized && !this.dispatched); - }), + } - parent: belongsTo('job', { inverse: 'children' }), - children: hasMany('job', { inverse: 'parent' }), + @belongsTo('job', { inverse: 'children' }) parent; + @hasMany('job', { inverse: 'parent' }) children; // The parent job name is prepended to child launch job names - trimmedName: computed('name', 'parent', function() { + @computed('name', 'parent') + get trimmedName() { return this.get('parent.content') ? this.name.replace(/.+?\//, '') : this.name; - }), + } // A composite of type and other job attributes to determine // a better type descriptor for human interpretation rather // than for scheduling. - displayType: computed('type', 'periodic', 'parameterized', function() { + @computed('type', 'periodic', 'parameterized') + get displayType() { if (this.periodic) { return 'periodic'; } else if (this.parameterized) { return 'parameterized'; } return this.type; - }), + } // A composite of type and other job attributes to determine // type for templating rather than scheduling - templateType: computed( - 'type', - 'periodic', - 'parameterized', - 'parent.{periodic,parameterized}', - function() { - const type = this.type; + @computed('type', 'periodic', 'parameterized', 'parent.{periodic,parameterized}') + get templateType() { + const type = this.type; - if (this.get('parent.periodic')) { - return 'periodic-child'; - } else if (this.get('parent.parameterized')) { - return 'parameterized-child'; - } else if (this.periodic) { - return 'periodic'; - } else if (this.parameterized) { - return 'parameterized'; - } else if (JOB_TYPES.includes(type)) { - // Guard against the API introducing a new type before the UI - // is prepared to handle it. - return this.type; - } - - // A fail-safe in the event the API introduces a new type. - return 'service'; + if (this.get('parent.periodic')) { + return 'periodic-child'; + } else if (this.get('parent.parameterized')) { + return 'parameterized-child'; + } else if (this.periodic) { + return 'periodic'; + } else if (this.parameterized) { + return 'parameterized'; + } else if (JOB_TYPES.includes(type)) { + // Guard against the API introducing a new type before the UI + // is prepared to handle it. + return this.type; } - ), - datacenters: attr(), - taskGroups: fragmentArray('task-group', { defaultValue: () => [] }), - summary: belongsTo('job-summary'), + // A fail-safe in the event the API introduces a new type. + return 'service'; + } + + @attr() datacenters; + @fragmentArray('task-group', { defaultValue: () => [] }) taskGroups; + @belongsTo('job-summary') summary; // A job model created from the jobs list response will be lacking // task groups. This is an indicator that it needs to be reloaded // if task group information is important. - isPartial: equal('taskGroups.length', 0), + @equal('taskGroups.length', 0) isPartial; // If a job has only been loaded through the list request, the task groups // are still unknown. However, the count of task groups is available through // the job-summary model which is embedded in the jobs list response. - taskGroupCount: or('taskGroups.length', 'taskGroupSummaries.length'), + @or('taskGroups.length', 'taskGroupSummaries.length') taskGroupCount; // 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.totalChildren'), + @alias('summary.taskGroupSummaries') taskGroupSummaries; + @alias('summary.queuedAllocs') queuedAllocs; + @alias('summary.startingAllocs') startingAllocs; + @alias('summary.runningAllocs') runningAllocs; + @alias('summary.completeAllocs') completeAllocs; + @alias('summary.failedAllocs') failedAllocs; + @alias('summary.lostAllocs') lostAllocs; + @alias('summary.totalAllocs') totalAllocs; + @alias('summary.pendingChildren') pendingChildren; + @alias('summary.runningChildren') runningChildren; + @alias('summary.deadChildren') deadChildren; + @alias('summary.totalChildren') totalChildren; - version: attr('number'), + @attr('number') version; - versions: hasMany('job-versions'), - allocations: hasMany('allocations'), - deployments: hasMany('deployments'), - evaluations: hasMany('evaluations'), - namespace: belongsTo('namespace'), + @hasMany('job-versions') versions; + @hasMany('allocations') allocations; + @hasMany('deployments') deployments; + @hasMany('evaluations') evaluations; + @belongsTo('namespace') namespace; - drivers: computed('taskGroups.@each.drivers', function() { + @computed('taskGroups.@each.drivers') + get drivers() { return this.taskGroups .mapBy('drivers') .reduce((all, drivers) => { @@ -128,13 +129,14 @@ export default Model.extend({ return all; }, []) .uniq(); - }), + } - allocationsUnhealthyDrivers: mapBy('allocations', 'unhealthyDrivers'), + @mapBy('allocations', 'unhealthyDrivers') allocationsUnhealthyDrivers; // Getting all unhealthy drivers for a job can be incredibly expensive if the job // has many allocations. This can lead to making an API request for many nodes. - unhealthyDrivers: computed('allocationsUnhealthyDrivers.[]', function() { + @computed('allocationsUnhealthyDrivers.[]') + get unhealthyDrivers() { return this.allocations .mapBy('unhealthyDrivers') .reduce((all, drivers) => { @@ -142,23 +144,26 @@ export default Model.extend({ return all; }, []) .uniq(); - }), + } - hasBlockedEvaluation: computed('evaluations.@each.isBlocked', function() { + @computed('evaluations.@each.isBlocked') + get hasBlockedEvaluation() { return this.evaluations.toArray().some(evaluation => evaluation.get('isBlocked')); - }), + } - hasPlacementFailures: and('latestFailureEvaluation', 'hasBlockedEvaluation'), + @and('latestFailureEvaluation', 'hasBlockedEvaluation') hasPlacementFailures; - latestEvaluation: computed('evaluations.{@each.modifyIndex,isPending}', function() { + @computed('evaluations.{@each.modifyIndex,isPending}') + get latestEvaluation() { const evaluations = this.evaluations; if (!evaluations || evaluations.get('isPending')) { return null; } return evaluations.sortBy('modifyIndex').get('lastObject'); - }), + } - latestFailureEvaluation: computed('evaluations.{@each.modifyIndex,isPending}', function() { + @computed('evaluations.{@each.modifyIndex,isPending}') + get latestFailureEvaluation() { const evaluations = this.evaluations; if (!evaluations || evaluations.get('isPending')) { return null; @@ -170,44 +175,45 @@ export default Model.extend({ } return; - }), + } - supportsDeployments: equal('type', 'service'), + @equal('type', 'service') supportsDeployments; - latestDeployment: belongsTo('deployment', { inverse: 'jobForLatest' }), + @belongsTo('deployment', { inverse: 'jobForLatest' }) latestDeployment; - runningDeployment: computed('latestDeployment', 'latestDeployment.isRunning', function() { + @computed('latestDeployment', 'latestDeployment.isRunning') + get runningDeployment() { const latest = this.latestDeployment; if (latest.get('isRunning')) return latest; return; - }), + } fetchRawDefinition() { return this.store.adapterFor('job').fetchRawDefinition(this); - }, + } forcePeriodic() { return this.store.adapterFor('job').forcePeriodic(this); - }, + } stop() { return this.store.adapterFor('job').stop(this); - }, + } plan() { assert('A job must be parsed before planned', this._newDefinitionJSON); return this.store.adapterFor('job').plan(this); - }, + } run() { assert('A job must be parsed before ran', this._newDefinitionJSON); return this.store.adapterFor('job').run(this); - }, + } update() { assert('A job must be parsed before updated', this._newDefinitionJSON); return this.store.adapterFor('job').update(this); - }, + } parse() { const definition = this._newDefinition; @@ -237,7 +243,7 @@ export default Model.extend({ } return promise; - }, + } setIdByPayload(payload) { const namespace = payload.Namespace || 'default'; @@ -250,13 +256,14 @@ export default Model.extend({ if (namespaceRecord) { this.set('namespace', namespaceRecord); } - }, + } resetId() { this.set('id', JSON.stringify([this.plainId, this.get('namespace.name') || 'default'])); - }, + } - statusClass: computed('status', function() { + @computed('status') + get statusClass() { const classMap = { pending: 'is-pending', running: 'is-primary', @@ -264,20 +271,22 @@ export default Model.extend({ }; return classMap[this.status] || 'is-dark'; - }), + } - payload: attr('string'), - decodedPayload: computed('payload', function() { + @attr('string') payload; + + @computed('payload') + get decodedPayload() { // Lazily decode the base64 encoded payload return window.atob(this.payload || ''); - }), + } // An arbitrary HCL or JSON string that is used by the serializer to plan // and run this job. Used for both new job models and saved job models. - _newDefinition: attr('string'), + @attr('string') _newDefinition; // The new definition may be HCL, in which case the API will need to parse the // spec first. In order to preserve both the original HCL and the parsed response // that will be submitted to the create job endpoint, another prop is necessary. - _newDefinitionJSON: attr('string'), -}); + @attr('string') _newDefinitionJSON; +} diff --git a/ui/app/models/lifecycle.js b/ui/app/models/lifecycle.js index 0381be2f1..a112d4a60 100644 --- a/ui/app/models/lifecycle.js +++ b/ui/app/models/lifecycle.js @@ -2,9 +2,9 @@ import attr from 'ember-data/attr'; import Fragment from 'ember-data-model-fragments/fragment'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - task: fragmentOwner(), +export default class Lifecycle extends Fragment { + @fragmentOwner() task; - hook: attr('string'), - sidecar: attr('boolean'), -}); + @attr('string') hook; + @attr('boolean') sidecar; +} diff --git a/ui/app/models/namespace.js b/ui/app/models/namespace.js index b5b5f5750..253ca808e 100644 --- a/ui/app/models/namespace.js +++ b/ui/app/models/namespace.js @@ -2,8 +2,8 @@ import { readOnly } from '@ember/object/computed'; import Model from 'ember-data/model'; import attr from 'ember-data/attr'; -export default Model.extend({ - name: readOnly('id'), - hash: attr('string'), - description: attr('string'), -}); +export default class Namespace extends Model { + @readOnly('id') name; + @attr('string') hash; + @attr('string') description; +} diff --git a/ui/app/models/network.js b/ui/app/models/network.js index 4f6502683..2d0f637d3 100644 --- a/ui/app/models/network.js +++ b/ui/app/models/network.js @@ -2,11 +2,11 @@ import attr from 'ember-data/attr'; import Fragment from 'ember-data-model-fragments/fragment'; import { array } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - device: attr('string'), - cidr: attr('string'), - ip: attr('string'), - mode: attr('string'), - mbits: attr('number'), - ports: array(), -}); +export default class Network extends Fragment { + @attr('string') device; + @attr('string') cidr; + @attr('string') ip; + @attr('string') mode; + @attr('number') mbits; + @array() ports; +} diff --git a/ui/app/models/node-attributes.js b/ui/app/models/node-attributes.js index 11c199c65..2f24d9bb9 100644 --- a/ui/app/models/node-attributes.js +++ b/ui/app/models/node-attributes.js @@ -5,10 +5,11 @@ import flat from 'flat'; const { unflatten } = flat; -export default Fragment.extend({ - nodeAttributes: attr(), +export default class NodeAttributes extends Fragment { + @attr() nodeAttributes; - attributesStructured: computed('nodeAttributes', function() { + @computed('nodeAttributes') + get attributesStructured() { const original = this.nodeAttributes; if (!original) { @@ -23,7 +24,7 @@ export default Fragment.extend({ return obj; }, {}); return unflatten(attrs, { overwrite: true }); - }), + } unknownProperty(key) { // Returns the exact value in index 0 and the subtree in index 1 @@ -33,5 +34,5 @@ export default Fragment.extend({ if (this.attributesStructured) { return get(this.attributesStructured, key); } - }, -}); + } +} diff --git a/ui/app/models/node-driver.js b/ui/app/models/node-driver.js index b235f864f..4431985d5 100644 --- a/ui/app/models/node-driver.js +++ b/ui/app/models/node-driver.js @@ -1,26 +1,30 @@ +import classic from 'ember-classic-decorator'; import Fragment from 'ember-data-model-fragments/fragment'; -import { computed, get } from '@ember/object'; +import { get, computed } from '@ember/object'; import attr from 'ember-data/attr'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; import { fragment } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - node: fragmentOwner(), +@classic +export default class NodeDriver extends Fragment { + @fragmentOwner() node; - attributes: fragment('node-attributes'), + @fragment('node-attributes') attributes; - attributesShort: computed('name', 'attributes.attributesStructured', function() { + @computed('name', 'attributes.attributesStructured') + get attributesShort() { const attributes = this.get('attributes.attributesStructured'); return get(attributes, `driver.${this.name}`); - }), + } - name: attr('string'), - detected: attr('boolean', { defaultValue: false }), - healthy: attr('boolean', { defaultValue: false }), - healthDescription: attr('string'), - updateTime: attr('date'), + @attr('string') name; + @attr('boolean', { defaultValue: false }) detected; + @attr('boolean', { defaultValue: false }) healthy; + @attr('string') healthDescription; + @attr('date') updateTime; - healthClass: computed('healthy', function() { + @computed('healthy') + get healthClass() { return this.healthy ? 'running' : 'failed'; - }), -}); + } +} diff --git a/ui/app/models/node-event.js b/ui/app/models/node-event.js index 8fcf3cfad..254250f16 100644 --- a/ui/app/models/node-event.js +++ b/ui/app/models/node-event.js @@ -3,13 +3,13 @@ import Fragment from 'ember-data-model-fragments/fragment'; import attr from 'ember-data/attr'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - node: fragmentOwner(), +export default class NodeEvent extends Fragment { + @fragmentOwner() node; - message: attr('string'), - subsystem: attr('string'), - details: attr(), - time: attr('date'), + @attr('string') message; + @attr('string') subsystem; + @attr() details; + @attr('date') time; - driver: alias('details.driver'), -}); + @alias('details.driver') driver; +} diff --git a/ui/app/models/node.js b/ui/app/models/node.js index d272c7629..d56d28ca6 100644 --- a/ui/app/models/node.js +++ b/ui/app/models/node.js @@ -8,52 +8,63 @@ import RSVP from 'rsvp'; import shortUUIDProperty from '../utils/properties/short-uuid'; import ipParts from '../utils/ip-parts'; -export default Model.extend({ +export default class Node extends Model { // Available from list response - name: attr('string'), - datacenter: attr('string'), - nodeClass: attr('string'), - isDraining: attr('boolean'), - schedulingEligibility: attr('string'), - status: attr('string'), - statusDescription: attr('string'), - shortId: shortUUIDProperty('id'), - modifyIndex: attr('number'), + @attr('string') name; + @attr('string') datacenter; + @attr('string') nodeClass; + @attr('boolean') isDraining; + @attr('string') schedulingEligibility; + @attr('string') status; + @attr('string') statusDescription; + @shortUUIDProperty('id') shortId; + @attr('number') modifyIndex; // Available from single response - httpAddr: attr('string'), - tlsEnabled: attr('boolean'), - attributes: fragment('node-attributes'), - meta: fragment('node-attributes'), - resources: fragment('resources'), - reserved: fragment('resources'), - drainStrategy: fragment('drain-strategy'), + @attr('string') httpAddr; + @attr('boolean') tlsEnabled; + @fragment('node-attributes') attributes; + @fragment('node-attributes') meta; + @fragment('resources') resources; + @fragment('resources') reserved; + @fragment('drain-strategy') drainStrategy; - isEligible: equal('schedulingEligibility', 'eligible'), + @equal('schedulingEligibility', 'eligible') isEligible; - address: computed('httpAddr', function() { + @computed('httpAddr') + get address() { return ipParts(this.httpAddr).address; - }), + } - port: computed('httpAddr', function() { + @computed('httpAddr') + get port() { return ipParts(this.httpAddr).port; - }), + } - isPartial: computed('httpAddr', function() { + @computed('httpAddr') + get isPartial() { return this.httpAddr == null; - }), + } - allocations: hasMany('allocations', { inverse: 'node' }), - completeAllocations: computed('allocations.@each.clientStatus', function() { + @hasMany('allocations', { inverse: 'node' }) allocations; + + @computed('allocations.@each.clientStatus') + get completeAllocations() { return this.allocations.filterBy('clientStatus', 'complete'); - }), - runningAllocations: computed('allocations.@each.isRunning', function() { + } + + @computed('allocations.@each.isRunning') + get runningAllocations() { return this.allocations.filterBy('isRunning'); - }), - migratingAllocations: computed('allocations.@each.{isMigrating,isRunning}', function() { + } + + @computed('allocations.@each.{isMigrating,isRunning}') + get migratingAllocations() { return this.allocations.filter(alloc => alloc.isRunning && alloc.isMigrating); - }), - lastMigrateTime: computed('allocations.@each.{isMigrating,isRunning,modifyTime}', function() { + } + + @computed('allocations.@each.{isMigrating,isRunning,modifyTime}') + get lastMigrateTime() { const allocation = this.allocations .filterBy('isRunning', false) .filterBy('isMigrating') @@ -64,27 +75,31 @@ export default Model.extend({ } return; - }), + } - drivers: fragmentArray('node-driver'), - events: fragmentArray('node-event'), - hostVolumes: fragmentArray('host-volume'), + @fragmentArray('node-driver') drivers; + @fragmentArray('node-event') events; + @fragmentArray('host-volume') hostVolumes; - detectedDrivers: computed('drivers.@each.detected', function() { + @computed('drivers.@each.detected') + get detectedDrivers() { return this.drivers.filterBy('detected'); - }), + } - unhealthyDrivers: computed('detectedDrivers.@each.healthy', function() { + @computed('detectedDrivers.@each.healthy') + get unhealthyDrivers() { return this.detectedDrivers.filterBy('healthy', false); - }), + } - unhealthyDriverNames: computed('unhealthyDrivers.@each.name', function() { + @computed('unhealthyDrivers.@each.name') + get unhealthyDriverNames() { return this.unhealthyDrivers.mapBy('name'); - }), + } // A status attribute that includes states not included in node status. // Useful for coloring and sorting nodes - compositeStatus: computed('isDraining', 'isEligible', 'status', function() { + @computed('isDraining', 'isEligible', 'status') + get compositeStatus() { if (this.isDraining) { return 'draining'; } else if (!this.isEligible) { @@ -92,9 +107,10 @@ export default Model.extend({ } else { return this.status; } - }), + } - compositeStatusIcon: computed('isDraining', 'isEligible', 'status', function() { + @computed('isDraining', 'isEligible', 'status') + get compositeStatusIcon() { if (this.isDraining || !this.isEligible) { return 'alert-circle-fill'; } else if (this.status === 'down') { @@ -103,31 +119,31 @@ export default Model.extend({ return 'node-init-circle-fill'; } return 'check-circle-fill'; - }), + } setEligible() { if (this.isEligible) return RSVP.resolve(); // Optimistically update schedulingEligibility for immediate feedback this.set('schedulingEligibility', 'eligible'); return this.store.adapterFor('node').setEligible(this); - }, + } setIneligible() { if (!this.isEligible) return RSVP.resolve(); // Optimistically update schedulingEligibility for immediate feedback this.set('schedulingEligibility', 'ineligible'); return this.store.adapterFor('node').setIneligible(this); - }, + } drain(drainSpec) { return this.store.adapterFor('node').drain(this, drainSpec); - }, + } forceDrain(drainSpec) { return this.store.adapterFor('node').forceDrain(this, drainSpec); - }, + } cancelDrain() { return this.store.adapterFor('node').cancelDrain(this); - }, -}); + } +} diff --git a/ui/app/models/placement-failure.js b/ui/app/models/placement-failure.js index d711166b1..6a1f328ac 100644 --- a/ui/app/models/placement-failure.js +++ b/ui/app/models/placement-failure.js @@ -1,20 +1,20 @@ import attr from 'ember-data/attr'; import Fragment from 'ember-data-model-fragments/fragment'; -export default Fragment.extend({ - name: attr('string'), +export default class PlacementFailure extends Fragment { + @attr('string') name; - coalescedFailures: attr('number'), + @attr('number') coalescedFailures; - nodesEvaluated: attr('number'), - nodesExhausted: attr('number'), + @attr('number') nodesEvaluated; + @attr('number') nodesExhausted; - // Maps keyed by relevant dimension (dc, class, constraint, etc) with count values - nodesAvailable: attr(), - classFiltered: attr(), - constraintFiltered: attr(), - classExhausted: attr(), - dimensionExhausted: attr(), - quotaExhausted: attr(), - scores: attr(), -}); + // Maps keyed by relevant dimension (dc, class, constraint, etc)ith count values + @attr() nodesAvailable; + @attr() classFiltered; + @attr() constraintFiltered; + @attr() classExhausted; + @attr() dimensionExhausted; + @attr() quotaExhausted; + @attr() scores; +} diff --git a/ui/app/models/plugin.js b/ui/app/models/plugin.js index 6042b2f68..4bbb29ba0 100644 --- a/ui/app/models/plugin.js +++ b/ui/app/models/plugin.js @@ -3,28 +3,30 @@ import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; -export default Model.extend({ - plainId: attr('string'), +export default class Plugin extends Model { + @attr('string') plainId; - topologies: attr(), - provider: attr('string'), - version: attr('string'), + @attr() topologies; + @attr('string') provider; + @attr('string') version; - controllers: fragmentArray('storage-controller', { defaultValue: () => [] }), - nodes: fragmentArray('storage-node', { defaultValue: () => [] }), + @fragmentArray('storage-controller', { defaultValue: () => [] }) controllers; + @fragmentArray('storage-node', { defaultValue: () => [] }) nodes; - controllerRequired: attr('boolean'), - controllersHealthy: attr('number'), - controllersExpected: attr('number'), + @attr('boolean') controllerRequired; + @attr('number') controllersHealthy; + @attr('number') controllersExpected; - controllersHealthyProportion: computed('controllersHealthy', 'controllersExpected', function() { + @computed('controllersHealthy', 'controllersExpected') + get controllersHealthyProportion() { return this.controllersHealthy / this.controllersExpected; - }), + } - nodesHealthy: attr('number'), - nodesExpected: attr('number'), + @attr('number') nodesHealthy; + @attr('number') nodesExpected; - nodesHealthyProportion: computed('nodesHealthy', 'nodesExpected', function() { + @computed('nodesHealthy', 'nodesExpected') + get nodesHealthyProportion() { return this.nodesHealthy / this.nodesExpected; - }), -}); + } +} diff --git a/ui/app/models/policy.js b/ui/app/models/policy.js index 8b333617b..444ac5ea9 100644 --- a/ui/app/models/policy.js +++ b/ui/app/models/policy.js @@ -1,9 +1,9 @@ import Model from 'ember-data/model'; import attr from 'ember-data/attr'; -export default Model.extend({ - name: attr('string'), - description: attr('string'), - rules: attr('string'), - rulesJSON: attr(), -}); +export default class Policy extends Model { + @attr('string') name; + @attr('string') description; + @attr('string') rules; + @attr() rulesJSON; +} diff --git a/ui/app/models/reschedule-event.js b/ui/app/models/reschedule-event.js index 911fe7898..6ad1d46b7 100644 --- a/ui/app/models/reschedule-event.js +++ b/ui/app/models/reschedule-event.js @@ -3,14 +3,14 @@ import attr from 'ember-data/attr'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; import shortUUIDProperty from '../utils/properties/short-uuid'; -export default Fragment.extend({ - allocation: fragmentOwner(), +export default class RescheduleEvent extends Fragment { + @fragmentOwner() allocation; - previousAllocationId: attr('string'), - previousNodeId: attr('string'), - time: attr('date'), - delay: attr('string'), + @attr('string') previousAllocationId; + @attr('string') previousNodeId; + @attr('date') time; + @attr('string') delay; - previousAllocationShortId: shortUUIDProperty('previousAllocationId'), - previousNodeShortId: shortUUIDProperty('previousNodeShortId'), -}); + @shortUUIDProperty('previousAllocationId') previousAllocationShortId; + @shortUUIDProperty('previousNodeShortId') previousNodeShortId; +} diff --git a/ui/app/models/resources.js b/ui/app/models/resources.js index 5bb900ee4..9d6082009 100644 --- a/ui/app/models/resources.js +++ b/ui/app/models/resources.js @@ -2,10 +2,10 @@ import attr from 'ember-data/attr'; import Fragment from 'ember-data-model-fragments/fragment'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - cpu: attr('number'), - memory: attr('number'), - disk: attr('number'), - iops: attr('number'), - networks: fragmentArray('network', { defaultValue: () => [] }), -}); +export default class Resources extends Fragment { + @attr('number') cpu; + @attr('number') memory; + @attr('number') disk; + @attr('number') iops; + @fragmentArray('network', { defaultValue: () => [] }) networks; +} diff --git a/ui/app/models/service.js b/ui/app/models/service.js index 1743c8180..e6f811a4a 100644 --- a/ui/app/models/service.js +++ b/ui/app/models/service.js @@ -2,9 +2,9 @@ import attr from 'ember-data/attr'; import Fragment from 'ember-data-model-fragments/fragment'; import { fragment } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - name: attr('string'), - portLabel: attr('string'), - tags: attr(), - connect: fragment('consul-connect'), -}); +export default class Service extends Fragment { + @attr('string') name; + @attr('string') portLabel; + @attr() tags; + @fragment('consul-connect') connect; +} diff --git a/ui/app/models/sidecar-proxy-upstream.js b/ui/app/models/sidecar-proxy-upstream.js index 76d0b0d4f..0205497f5 100644 --- a/ui/app/models/sidecar-proxy-upstream.js +++ b/ui/app/models/sidecar-proxy-upstream.js @@ -1,7 +1,7 @@ import Fragment from 'ember-data-model-fragments/fragment'; import attr from 'ember-data/attr'; -export default Fragment.extend({ - destinationName: attr('string'), - localBindPort: attr('string'), -}); +export default class SidecarProxyUpstream extends Fragment { + @attr('string') destinationName; + @attr('string') localBindPort; +} diff --git a/ui/app/models/sidecar-proxy.js b/ui/app/models/sidecar-proxy.js index 5370fdd66..e080f3123 100644 --- a/ui/app/models/sidecar-proxy.js +++ b/ui/app/models/sidecar-proxy.js @@ -1,6 +1,6 @@ import Fragment from 'ember-data-model-fragments/fragment'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - upstreams: fragmentArray('sidecar-proxy-upstream'), -}); +export default class SidecarProxy extends Fragment { + @fragmentArray('sidecar-proxy-upstream') upstreams; +} diff --git a/ui/app/models/sidecar-service.js b/ui/app/models/sidecar-service.js index 5e271d01e..3350f77ac 100644 --- a/ui/app/models/sidecar-service.js +++ b/ui/app/models/sidecar-service.js @@ -1,6 +1,6 @@ import Fragment from 'ember-data-model-fragments/fragment'; import { fragment } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - proxy: fragment('sidecar-proxy'), -}); +export default class SidecarService extends Fragment { + @fragment('sidecar-proxy') proxy; +} diff --git a/ui/app/models/storage-controller.js b/ui/app/models/storage-controller.js index 4d7c44114..9406d0ec2 100644 --- a/ui/app/models/storage-controller.js +++ b/ui/app/models/storage-controller.js @@ -3,24 +3,24 @@ import { belongsTo } from 'ember-data/relationships'; import Fragment from 'ember-data-model-fragments/fragment'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - plugin: fragmentOwner(), +export default class StorageController extends Fragment { + @fragmentOwner() plugin; - node: belongsTo('node'), - allocID: attr('string'), + @belongsTo('node') node; + @attr('string') allocID; - provider: attr('string'), - version: attr('string'), - healthy: attr('boolean'), - healthDescription: attr('string'), - updateTime: attr('date'), - requiresControllerPlugin: attr('boolean'), - requiresTopologies: attr('boolean'), + @attr('string') provider; + @attr('string') version; + @attr('boolean') healthy; + @attr('string') healthDescription; + @attr('date') updateTime; + @attr('boolean') requiresControllerPlugin; + @attr('boolean') requiresTopologies; - controllerInfo: attr(), + @attr() controllerInfo; // Fragments can't have relationships, so provider a manual getter instead. async getAllocation() { return this.store.findRecord('allocation', this.allocID); - }, -}); + } +} diff --git a/ui/app/models/storage-node.js b/ui/app/models/storage-node.js index 65e43050e..1c3637c49 100644 --- a/ui/app/models/storage-node.js +++ b/ui/app/models/storage-node.js @@ -3,24 +3,24 @@ import { belongsTo } from 'ember-data/relationships'; import Fragment from 'ember-data-model-fragments/fragment'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - plugin: fragmentOwner(), +export default class StorageNode extends Fragment { + @fragmentOwner() plugin; - node: belongsTo('node'), - allocID: attr('string'), + @belongsTo('node') node; + @attr('string') allocID; - provider: attr('string'), - version: attr('string'), - healthy: attr('boolean'), - healthDescription: attr('string'), - updateTime: attr('date'), - requiresControllerPlugin: attr('boolean'), - requiresTopologies: attr('boolean'), + @attr('string') provider; + @attr('string') version; + @attr('boolean') healthy; + @attr('string') healthDescription; + @attr('date') updateTime; + @attr('boolean') requiresControllerPlugin; + @attr('boolean') requiresTopologies; - nodeInfo: attr(), + @attr() nodeInfo; // Fragments can't have relationships, so provider a manual getter instead. async getAllocation() { return this.store.findRecord('allocation', this.allocID); - }, -}); + } +} diff --git a/ui/app/models/task-event.js b/ui/app/models/task-event.js index 124d70ebc..794c490b6 100644 --- a/ui/app/models/task-event.js +++ b/ui/app/models/task-event.js @@ -2,15 +2,15 @@ import Fragment from 'ember-data-model-fragments/fragment'; import attr from 'ember-data/attr'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - state: fragmentOwner(), +export default class TaskEvent extends Fragment { + @fragmentOwner() state; - type: attr('string'), - signal: attr('number'), - exitCode: attr('number'), + @attr('string') type; + @attr('number') signal; + @attr('number') exitCode; - time: attr('date'), - timeNanos: attr('number'), + @attr('date') time; + @attr('number') timeNanos; - message: attr('string'), -}); + @attr('string') message; +} diff --git a/ui/app/models/task-group-deployment-summary.js b/ui/app/models/task-group-deployment-summary.js index 21f795f87..b245e7127 100644 --- a/ui/app/models/task-group-deployment-summary.js +++ b/ui/app/models/task-group-deployment-summary.js @@ -3,25 +3,25 @@ import Fragment from 'ember-data-model-fragments/fragment'; import attr from 'ember-data/attr'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - deployment: fragmentOwner(), +export default class TaskGroupDeploymentSummary extends Fragment { + @fragmentOwner() deployment; - name: attr('string'), + @attr('string') name; - autoRevert: attr('boolean'), - promoted: attr('boolean'), - requiresPromotion: gt('desiredCanaries', 0), + @attr('boolean') autoRevert; + @attr('boolean') promoted; + @gt('desiredCanaries', 0) requiresPromotion; // The list of canary allocation IDs // hasMany is not supported in fragments - placedCanaryAllocations: attr({ defaultValue: () => [] }), + @attr({ defaultValue: () => [] }) placedCanaryAllocations; - placedCanaries: alias('placedCanaryAllocations.length'), - desiredCanaries: attr('number'), - desiredTotal: attr('number'), - placedAllocs: attr('number'), - healthyAllocs: attr('number'), - unhealthyAllocs: attr('number'), + @alias('placedCanaryAllocations.length') placedCanaries; + @attr('number') desiredCanaries; + @attr('number') desiredTotal; + @attr('number') placedAllocs; + @attr('number') healthyAllocs; + @attr('number') unhealthyAllocs; - requireProgressBy: attr('date'), -}); + @attr('date') requireProgressBy; +} diff --git a/ui/app/models/task-group-summary.js b/ui/app/models/task-group-summary.js index 9e713b8ab..882e842bd 100644 --- a/ui/app/models/task-group-summary.js +++ b/ui/app/models/task-group-summary.js @@ -1,27 +1,28 @@ -import { collect, sum } from '@ember/object/computed'; +import { sum, collect } from '@ember/object/computed'; import Fragment from 'ember-data-model-fragments/fragment'; import attr from 'ember-data/attr'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - job: fragmentOwner(), - name: attr('string'), +export default class TaskGroupSummary extends Fragment { + @fragmentOwner() job; + @attr('string') name; - queuedAllocs: attr('number'), - startingAllocs: attr('number'), - runningAllocs: attr('number'), - completeAllocs: attr('number'), - failedAllocs: attr('number'), - lostAllocs: attr('number'), + @attr('number') queuedAllocs; + @attr('number') startingAllocs; + @attr('number') runningAllocs; + @attr('number') completeAllocs; + @attr('number') failedAllocs; + @attr('number') lostAllocs; - allocsList: collect( + @collect( 'queuedAllocs', 'startingAllocs', 'runningAllocs', 'completeAllocs', 'failedAllocs', 'lostAllocs' - ), + ) + allocsList; - totalAllocs: sum('allocsList'), -}); + @sum('allocsList') totalAllocs; +} diff --git a/ui/app/models/task-group.js b/ui/app/models/task-group.js index 361e45f7e..1e60913f2 100644 --- a/ui/app/models/task-group.js +++ b/ui/app/models/task-group.js @@ -3,45 +3,52 @@ import Fragment from 'ember-data-model-fragments/fragment'; import attr from 'ember-data/attr'; import { fragmentOwner, fragmentArray } from 'ember-data-model-fragments/attributes'; import sumAggregation from '../utils/properties/sum-aggregation'; +import classic from 'ember-classic-decorator'; const maybe = arr => arr || []; -export default Fragment.extend({ - job: fragmentOwner(), +@classic +export default class TaskGroup extends Fragment { + @fragmentOwner() job; - name: attr('string'), - count: attr('number'), + @attr('string') name; + @attr('number') count; - tasks: fragmentArray('task'), + @fragmentArray('task') tasks; - services: fragmentArray('service'), + @fragmentArray('service') services; - volumes: fragmentArray('volume-definition'), + @fragmentArray('volume-definition') volumes; - drivers: computed('tasks.@each.driver', function() { + @computed('tasks.@each.driver') + get drivers() { return this.tasks.mapBy('driver').uniq(); - }), + } - allocations: computed('job.allocations.@each.taskGroup', function() { + @computed('job.allocations.@each.taskGroup') + get allocations() { return maybe(this.get('job.allocations')).filterBy('taskGroupName', this.name); - }), + } - reservedCPU: sumAggregation('tasks', 'reservedCPU'), - reservedMemory: sumAggregation('tasks', 'reservedMemory'), - reservedDisk: sumAggregation('tasks', 'reservedDisk'), + @sumAggregation('tasks', 'reservedCPU') reservedCPU; + @sumAggregation('tasks', 'reservedMemory') reservedMemory; + @sumAggregation('tasks', 'reservedDisk') reservedDisk; - reservedEphemeralDisk: attr('number'), + @attr('number') reservedEphemeralDisk; - placementFailures: computed('job.latestFailureEvaluation.failedTGAllocs.[]', function() { + @computed('job.latestFailureEvaluation.failedTGAllocs.[]') + get placementFailures() { const placementFailures = this.get('job.latestFailureEvaluation.failedTGAllocs'); return placementFailures && placementFailures.findBy('name', this.name); - }), + } - queuedOrStartingAllocs: computed('summary.{queuedAllocs,startingAllocs}', function() { + @computed('summary.{queuedAllocs,startingAllocs}') + get queuedOrStartingAllocs() { return this.get('summary.queuedAllocs') + this.get('summary.startingAllocs'); - }), + } - summary: computed('job.taskGroupSummaries.[]', function() { + @computed('job.taskGroupSummaries.[]') + get summary() { return maybe(this.get('job.taskGroupSummaries')).findBy('name', this.name); - }), -}); + } +} diff --git a/ui/app/models/task-state.js b/ui/app/models/task-state.js index fb9736ab7..f23985463 100644 --- a/ui/app/models/task-state.js +++ b/ui/app/models/task-state.js @@ -3,41 +3,47 @@ import { alias, none, and } from '@ember/object/computed'; import Fragment from 'ember-data-model-fragments/fragment'; import attr from 'ember-data/attr'; import { fragment, fragmentOwner, fragmentArray } from 'ember-data-model-fragments/attributes'; +import classic from 'ember-classic-decorator'; -export default Fragment.extend({ - allocation: fragmentOwner(), +@classic +export default class TaskState extends Fragment { + @fragmentOwner() allocation; - name: attr('string'), - state: attr('string'), - startedAt: attr('date'), - finishedAt: attr('date'), - failed: attr('boolean'), + @attr('string') name; + @attr('string') state; + @attr('date') startedAt; + @attr('date') finishedAt; + @attr('boolean') failed; - isActive: none('finishedAt'), - isRunning: and('isActive', 'allocation.isRunning'), + @none('finishedAt') isActive; + @and('isActive', 'allocation.isRunning') isRunning; - isConnectProxy: computed('task.kind', function() { + @computed('task.kind') + get isConnectProxy() { return (this.get('task.kind') || '').startsWith('connect-proxy:'); - }), + } - task: computed('name', 'allocation.taskGroup.tasks.[]', function() { + @computed('name', 'allocation.taskGroup.tasks.[]') + get task() { const tasks = this.get('allocation.taskGroup.tasks'); return tasks && tasks.findBy('name', this.name); - }), + } - driver: alias('task.driver'), + @alias('task.driver') driver; // TaskState represents a task running on a node, so in addition to knowing the // driver via the task, the health of the driver is also known via the node - driverStatus: computed('task.driver', 'allocation.node.drivers.[]', function() { + @computed('task.driver', 'allocation.node.drivers.[]') + get driverStatus() { const nodeDrivers = this.get('allocation.node.drivers') || []; return nodeDrivers.findBy('name', this.get('task.driver')); - }), + } - resources: fragment('resources'), - events: fragmentArray('task-event'), + @fragment('resources') resources; + @fragmentArray('task-event') events; - stateClass: computed('state', function() { + @computed('state') + get stateClass() { const classMap = { pending: 'is-pending', running: 'is-primary', @@ -46,9 +52,9 @@ export default Fragment.extend({ }; return classMap[this.state] || 'is-dark'; - }), + } restart() { return this.allocation.restart(this.name); - }, -}); + } +} diff --git a/ui/app/models/task.js b/ui/app/models/task.js index 7d5061272..7c2b95a6f 100644 --- a/ui/app/models/task.js +++ b/ui/app/models/task.js @@ -3,25 +3,26 @@ import Fragment from 'ember-data-model-fragments/fragment'; import { fragment, fragmentArray, fragmentOwner } from 'ember-data-model-fragments/attributes'; import { computed } from '@ember/object'; -export default Fragment.extend({ - taskGroup: fragmentOwner(), +export default class Task extends Fragment { + @fragmentOwner() taskGroup; - name: attr('string'), - driver: attr('string'), - kind: attr('string'), + @attr('string') name; + @attr('string') driver; + @attr('string') kind; - lifecycle: fragment('lifecycle'), + @fragment('lifecycle') lifecycle; - lifecycleName: computed('lifecycle', 'lifecycle.sidecar', function() { + @computed('lifecycle', 'lifecycle.sidecar') + get lifecycleName() { if (this.lifecycle && this.lifecycle.sidecar) return 'sidecar'; if (this.lifecycle && this.lifecycle.hook === 'prestart') return 'prestart'; return 'main'; - }), + } - reservedMemory: attr('number'), - reservedCPU: attr('number'), - reservedDisk: attr('number'), - reservedEphemeralDisk: attr('number'), + @attr('number') reservedMemory; + @attr('number') reservedCPU; + @attr('number') reservedDisk; + @attr('number') reservedEphemeralDisk; - volumeMounts: fragmentArray('volume-mount', { defaultValue: () => [] }), -}); + @fragmentArray('volume-mount', { defaultValue: () => [] }) volumeMounts; +} diff --git a/ui/app/models/token.js b/ui/app/models/token.js index 37db199ae..2302dd8d6 100644 --- a/ui/app/models/token.js +++ b/ui/app/models/token.js @@ -3,14 +3,14 @@ import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { hasMany } from 'ember-data/relationships'; -export default Model.extend({ - secret: attr('string'), - name: attr('string'), - global: attr('boolean'), - createTime: attr('date'), - type: attr('string'), - policies: hasMany('policy'), - policyNames: attr(), +export default class Token extends Model { + @attr('string') secret; + @attr('string') name; + @attr('boolean') global; + @attr('date') createTime; + @attr('string') type; + @hasMany('policy') policies; + @attr() policyNames; - accessor: alias('id'), -}); + @alias('id') accessor; +} diff --git a/ui/app/models/volume-definition.js b/ui/app/models/volume-definition.js index b682b4c3a..ce94e541e 100644 --- a/ui/app/models/volume-definition.js +++ b/ui/app/models/volume-definition.js @@ -3,15 +3,15 @@ import attr from 'ember-data/attr'; import Fragment from 'ember-data-model-fragments/fragment'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - taskGroup: fragmentOwner(), +export default class VolumeDefinition extends Fragment { + @fragmentOwner() taskGroup; - name: attr('string'), + @attr('string') name; - source: attr('string'), - type: attr('string'), - readOnly: attr('boolean'), + @attr('string') source; + @attr('string') type; + @attr('boolean') readOnly; - isCSI: equal('type', 'csi'), - namespace: alias('taskGroup.job.namespace'), -}); + @equal('type', 'csi') isCSI; + @alias('taskGroup.job.namespace') namespace; +} diff --git a/ui/app/models/volume-mount.js b/ui/app/models/volume-mount.js index 107c093f5..7fa78d465 100644 --- a/ui/app/models/volume-mount.js +++ b/ui/app/models/volume-mount.js @@ -4,23 +4,24 @@ import attr from 'ember-data/attr'; import Fragment from 'ember-data-model-fragments/fragment'; import { fragmentOwner } from 'ember-data-model-fragments/attributes'; -export default Fragment.extend({ - task: fragmentOwner(), +export default class VolumeMount extends Fragment { + @fragmentOwner() task; - volume: attr('string'), + @attr('string') volume; - volumeDeclaration: computed('task.taskGroup.volumes.@each.name', function() { + @computed('task.taskGroup.volumes.@each.name') + get volumeDeclaration() { return this.task.taskGroup.volumes.findBy('name', this.volume); - }), + } - isCSI: equal('volumeDeclaration.type', 'csi'), - source: alias('volumeDeclaration.source'), + @equal('volumeDeclaration.type', 'csi') isCSI; + @alias('volumeDeclaration.source') source; // Since CSI volumes are namespaced, the link intent of a volume mount will // be to the CSI volume with a namespace that matches this task's job's namespace. - namespace: alias('task.taskGroup.job.namespace'), + @alias('task.taskGroup.job.namespace') namespace; - destination: attr('string'), - propagationMode: attr('string'), - readOnly: attr('boolean'), -}); + @attr('string') destination; + @attr('string') propagationMode; + @attr('boolean') readOnly; +} diff --git a/ui/app/models/volume.js b/ui/app/models/volume.js index 167fc6502..ed2b285c3 100644 --- a/ui/app/models/volume.js +++ b/ui/app/models/volume.js @@ -3,44 +3,47 @@ import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import { belongsTo, hasMany } from 'ember-data/relationships'; -export default Model.extend({ - plainId: attr('string'), - name: attr('string'), +export default class Volume extends Model { + @attr('string') plainId; + @attr('string') name; - namespace: belongsTo('namespace'), - plugin: belongsTo('plugin'), + @belongsTo('namespace') namespace; + @belongsTo('plugin') plugin; - writeAllocations: hasMany('allocation'), - readAllocations: hasMany('allocation'), + @hasMany('allocation') writeAllocations; + @hasMany('allocation') readAllocations; - allocations: computed('writeAllocations.[]', 'readAllocations.[]', function() { + @computed('writeAllocations.[]', 'readAllocations.[]') + get allocations() { return [...this.writeAllocations.toArray(), ...this.readAllocations.toArray()]; - }), + } - externalId: attr('string'), - topologies: attr(), - accessMode: attr('string'), - attachmentMode: attr('string'), - schedulable: attr('boolean'), - provider: attr('string'), - version: attr('string'), + @attr('string') externalId; + @attr() topologies; + @attr('string') accessMode; + @attr('string') attachmentMode; + @attr('boolean') schedulable; + @attr('string') provider; + @attr('string') version; - controllerRequired: attr('boolean'), - controllersHealthy: attr('number'), - controllersExpected: attr('number'), + @attr('boolean') controllerRequired; + @attr('number') controllersHealthy; + @attr('number') controllersExpected; - controllersHealthyProportion: computed('controllersHealthy', 'controllersExpected', function() { + @computed('controllersHealthy', 'controllersExpected') + get controllersHealthyProportion() { return this.controllersHealthy / this.controllersExpected; - }), + } - nodesHealthy: attr('number'), - nodesExpected: attr('number'), + @attr('number') nodesHealthy; + @attr('number') nodesExpected; - nodesHealthyProportion: computed('nodesHealthy', 'nodesExpected', function() { + @computed('nodesHealthy', 'nodesExpected') + get nodesHealthyProportion() { return this.nodesHealthy / this.nodesExpected; - }), + } - resourceExhausted: attr('number'), - createIndex: attr('number'), - modifyIndex: attr('number'), -}); + @attr('number') resourceExhausted; + @attr('number') createIndex; + @attr('number') modifyIndex; +} diff --git a/ui/app/router.js b/ui/app/router.js index 9645a7b70..51e162268 100644 --- a/ui/app/router.js +++ b/ui/app/router.js @@ -1,10 +1,10 @@ import EmberRouter from '@ember/routing/router'; import config from './config/environment'; -const Router = EmberRouter.extend({ - location: config.locationType, - rootURL: config.rootURL, -}); +class Router extends EmberRouter { + location = config.locationType; + rootURL = config.rootURL; +} Router.map(function() { this.route('exec', { path: '/exec/:job_name' }, function() { diff --git a/ui/app/routes/allocations/allocation.js b/ui/app/routes/allocations/allocation.js index c884af592..251f00726 100644 --- a/ui/app/routes/allocations/allocation.js +++ b/ui/app/routes/allocations/allocation.js @@ -6,12 +6,12 @@ import notifyError from 'nomad-ui/utils/notify-error'; import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; import { jobCrumbs } from 'nomad-ui/utils/breadcrumb-utils'; -export default Route.extend(WithWatchers, { +export default class AllocationRoute extends Route.extend(WithWatchers) { startWatchers(controller, model) { if (model) { controller.set('watcher', this.watch.perform(model)); } - }, + } // Allocation breadcrumbs extend from job / task group breadcrumbs // even though the route structure does not. @@ -37,16 +37,17 @@ export default Route.extend(WithWatchers, { args: ['allocations.allocation', model], }, ]; - }, + } model() { // Preload the job for the allocation since it's required for the breadcrumb trail - return this._super(...arguments) + return super + .model(...arguments) .then(allocation => allocation.get('job').then(() => allocation)) .catch(notifyError(this)); - }, + } - watch: watchRecord('allocation'), + @watchRecord('allocation') watch; - watchers: collect('watch'), -}); + @collect('watch') watchers; +} diff --git a/ui/app/routes/allocations/allocation/fs-root.js b/ui/app/routes/allocations/allocation/fs-root.js index eb85dc7c3..cc21ceb44 100644 --- a/ui/app/routes/allocations/allocation/fs-root.js +++ b/ui/app/routes/allocations/allocation/fs-root.js @@ -1,5 +1,5 @@ import FSRoute from './fs'; -export default FSRoute.extend({ - templateName: 'allocations/allocation/fs', -}); +export default class FsRootRoute extends FSRoute { + templateName = 'allocations/allocation/fs'; +} diff --git a/ui/app/routes/allocations/allocation/fs.js b/ui/app/routes/allocations/allocation/fs.js index f3580621a..ae1c67fe7 100644 --- a/ui/app/routes/allocations/allocation/fs.js +++ b/ui/app/routes/allocations/allocation/fs.js @@ -2,7 +2,7 @@ import Route from '@ember/routing/route'; import RSVP from 'rsvp'; import notifyError from 'nomad-ui/utils/notify-error'; -export default Route.extend({ +export default class FsRoute extends Route { model({ path = '/' }) { const decodedPath = decodeURIComponent(path); const allocation = this.modelFor('allocations.allocation'); @@ -33,10 +33,10 @@ export default Route.extend({ } }) .catch(notifyError(this)); - }, + } setupController(controller, { path, allocation, directoryEntries, isFile, stat } = {}) { - this._super(...arguments); + super.setupController(...arguments); controller.setProperties({ path, allocation, directoryEntries, isFile, stat }); - }, -}); + } +} diff --git a/ui/app/routes/allocations/allocation/index.js b/ui/app/routes/allocations/allocation/index.js index 323af0e82..e4de01cdb 100644 --- a/ui/app/routes/allocations/allocation/index.js +++ b/ui/app/routes/allocations/allocation/index.js @@ -1,6 +1,6 @@ import Route from '@ember/routing/route'; -export default Route.extend({ +export default class IndexRoute extends Route { setupController(controller, model) { // Suppress the preemptedByAllocation fetch error in the event it's a 404 if (model) { @@ -8,12 +8,12 @@ export default Route.extend({ model.preemptedByAllocation.then(setPreempter, setPreempter); } - return this._super(...arguments); - }, + return super.setupController(...arguments); + } resetController(controller, isExiting) { if (isExiting) { controller.watchNext.cancelAll(); } - }, -}); + } +} diff --git a/ui/app/routes/allocations/allocation/task.js b/ui/app/routes/allocations/allocation/task.js index d06a7b978..7f3eec93d 100644 --- a/ui/app/routes/allocations/allocation/task.js +++ b/ui/app/routes/allocations/allocation/task.js @@ -2,8 +2,8 @@ import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; import EmberError from '@ember/error'; -export default Route.extend({ - store: service(), +export default class TaskRoute extends Route { + @service store; breadcrumbs(model) { if (!model) return []; @@ -13,7 +13,7 @@ export default Route.extend({ args: ['allocations.allocation.task', model.get('allocation'), model], }, ]; - }, + } model({ name }) { const allocation = this.modelFor('allocations.allocation'); @@ -31,5 +31,5 @@ export default Route.extend({ } return task; - }, -}); + } +} diff --git a/ui/app/routes/allocations/allocation/task/fs-root.js b/ui/app/routes/allocations/allocation/task/fs-root.js index 81f1c1ee6..38e49355f 100644 --- a/ui/app/routes/allocations/allocation/task/fs-root.js +++ b/ui/app/routes/allocations/allocation/task/fs-root.js @@ -1,5 +1,5 @@ import FSRoute from './fs'; -export default FSRoute.extend({ - templateName: 'allocations/allocation/task/fs', -}); +export default class FsRootRoute extends FSRoute { + templateName = 'allocations/allocation/task/fs'; +} diff --git a/ui/app/routes/allocations/allocation/task/fs.js b/ui/app/routes/allocations/allocation/task/fs.js index 4827d5276..7fcbc8734 100644 --- a/ui/app/routes/allocations/allocation/task/fs.js +++ b/ui/app/routes/allocations/allocation/task/fs.js @@ -2,13 +2,15 @@ import Route from '@ember/routing/route'; import RSVP from 'rsvp'; import notifyError from 'nomad-ui/utils/notify-error'; -export default Route.extend({ +export default class FsRoute extends Route { model({ path = '/' }) { const decodedPath = decodeURIComponent(path); const taskState = this.modelFor('allocations.allocation.task'); const allocation = taskState.allocation; - const pathWithTaskName = `${taskState.name}${decodedPath.startsWith('/') ? '' : '/'}${decodedPath}`; + const pathWithTaskName = `${taskState.name}${ + decodedPath.startsWith('/') ? '' : '/' + }${decodedPath}`; if (!taskState.isRunning) { return { @@ -36,10 +38,10 @@ export default Route.extend({ } }) .catch(notifyError(this)); - }, + } setupController(controller, { path, taskState, directoryEntries, isFile, stat } = {}) { - this._super(...arguments); + super.setupController(...arguments); controller.setProperties({ path, taskState, directoryEntries, isFile, stat }); - }, -}); + } +} diff --git a/ui/app/routes/allocations/allocation/task/logs.js b/ui/app/routes/allocations/allocation/task/logs.js index 3b5ad2312..dde16232f 100644 --- a/ui/app/routes/allocations/allocation/task/logs.js +++ b/ui/app/routes/allocations/allocation/task/logs.js @@ -1,8 +1,8 @@ import Route from '@ember/routing/route'; -export default Route.extend({ +export default class LogsRoute extends Route { model() { - const task = this._super(...arguments); + const task = super.model(...arguments); return task && task.get('allocation.node').then(() => task); - }, -}); + } +} diff --git a/ui/app/routes/clients/client.js b/ui/app/routes/clients/client.js index c248c944d..64cf7d087 100644 --- a/ui/app/routes/clients/client.js +++ b/ui/app/routes/clients/client.js @@ -5,12 +5,12 @@ import notifyError from 'nomad-ui/utils/notify-error'; import { watchRecord, watchRelationship } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { - store: service(), +export default class ClientRoute extends Route.extend(WithWatchers) { + @service store; model() { - return this._super(...arguments).catch(notifyError(this)); - }, + return super.model(...arguments).catch(notifyError(this)); + } breadcrumbs(model) { if (!model) return []; @@ -20,20 +20,20 @@ export default Route.extend(WithWatchers, { args: ['clients.client', model.get('id')], }, ]; - }, + } afterModel(model) { if (model && model.get('isPartial')) { return model.reload().then(node => node.get('allocations')); } return model && model.get('allocations'); - }, + } setupController(controller, model) { controller.set('flagAsDraining', model && model.isDraining); - return this._super(...arguments); - }, + return super.setupController(...arguments); + } resetController(controller) { controller.setProperties({ @@ -45,17 +45,17 @@ export default Route.extend(WithWatchers, { showDrainUpdateNotification: false, showDrainStoppedNotification: false, }); - }, + } startWatchers(controller, model) { if (model) { controller.set('watchModel', this.watch.perform(model)); controller.set('watchAllocations', this.watchAllocations.perform(model)); } - }, + } - watch: watchRecord('node'), - watchAllocations: watchRelationship('allocations'), + @watchRecord('node') watch; + @watchRelationship('allocations') watchAllocations; - watchers: collect('watch', 'watchAllocations'), -}); + @collect('watch', 'watchAllocations') watchers; +} diff --git a/ui/app/routes/clients/index.js b/ui/app/routes/clients/index.js index 374c8d007..862c16b75 100644 --- a/ui/app/routes/clients/index.js +++ b/ui/app/routes/clients/index.js @@ -3,11 +3,11 @@ import { collect } from '@ember/object/computed'; import { watchAll } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class IndexRoute extends Route.extend(WithWatchers) { startWatchers(controller) { controller.set('watcher', this.watch.perform()); - }, + } - watch: watchAll('node'), - watchers: collect('watch'), -}); + @watchAll('node') watch; + @collect('watch') watchers; +} diff --git a/ui/app/routes/csi/index.js b/ui/app/routes/csi/index.js index f66008d19..3b77d0513 100644 --- a/ui/app/routes/csi/index.js +++ b/ui/app/routes/csi/index.js @@ -1,7 +1,7 @@ import Route from '@ember/routing/route'; -export default Route.extend({ +export default class IndexRoute extends Route { redirect() { this.transitionTo('csi.volumes'); - }, -}); + } +} diff --git a/ui/app/routes/csi/plugins/index.js b/ui/app/routes/csi/plugins/index.js index 2adff9d30..1155e7b80 100644 --- a/ui/app/routes/csi/plugins/index.js +++ b/ui/app/routes/csi/plugins/index.js @@ -3,11 +3,11 @@ import { collect } from '@ember/object/computed'; import { watchQuery } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class IndexRoute extends Route.extend(WithWatchers) { startWatchers(controller) { controller.set('modelWatch', this.watch.perform({ type: 'csi' })); - }, + } - watch: watchQuery('plugin'), - watchers: collect('watch'), -}); + @watchQuery('plugin') watch; + @collect('watch') watchers; +} diff --git a/ui/app/routes/csi/plugins/plugin.js b/ui/app/routes/csi/plugins/plugin.js index 3cdc3157d..5643b2fb7 100644 --- a/ui/app/routes/csi/plugins/plugin.js +++ b/ui/app/routes/csi/plugins/plugin.js @@ -2,11 +2,11 @@ import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; import notifyError from 'nomad-ui/utils/notify-error'; -export default Route.extend({ - store: service(), - system: service(), +export default class PluginRoute extends Route { + @service store; + @service system; - breadcrumbs: plugin => [ + breadcrumbs = plugin => [ { label: 'Plugins', args: ['csi.plugins'], @@ -15,13 +15,13 @@ export default Route.extend({ label: plugin.plainId, args: ['csi.plugins.plugin', plugin.plainId], }, - ], + ]; serialize(model) { return { plugin_name: model.get('plainId') }; - }, + } model(params) { return this.store.findRecord('plugin', `csi/${params.plugin_name}`).catch(notifyError(this)); - }, -}); + } +} diff --git a/ui/app/routes/csi/plugins/plugin/allocations.js b/ui/app/routes/csi/plugins/plugin/allocations.js index b46d2b5a5..f25dd1ec2 100644 --- a/ui/app/routes/csi/plugins/plugin/allocations.js +++ b/ui/app/routes/csi/plugins/plugin/allocations.js @@ -3,14 +3,14 @@ import { collect } from '@ember/object/computed'; import { watchRecord } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class AllocationsRoute extends Route.extend(WithWatchers) { startWatchers(controller, model) { if (!model) return; controller.set('watchers', { model: this.watch.perform(model), }); - }, + } resetController(controller, isExiting) { if (isExiting) { @@ -20,8 +20,8 @@ export default Route.extend(WithWatchers, { qpHealth: '', }); } - }, + } - watch: watchRecord('plugin'), - watchers: collect('watch'), -}); + @watchRecord('plugin') watch; + @collect('watch') watchers; +} diff --git a/ui/app/routes/csi/plugins/plugin/index.js b/ui/app/routes/csi/plugins/plugin/index.js index 350c6ac20..f80460f42 100644 --- a/ui/app/routes/csi/plugins/plugin/index.js +++ b/ui/app/routes/csi/plugins/plugin/index.js @@ -3,15 +3,15 @@ import { collect } from '@ember/object/computed'; import { watchRecord } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class IndexRoute extends Route.extend(WithWatchers) { startWatchers(controller, model) { if (!model) return; controller.set('watchers', { model: this.watch.perform(model), }); - }, + } - watch: watchRecord('plugin'), - watchers: collect('watch'), -}); + @watchRecord('plugin') watch; + @collect('watch') watchers; +} diff --git a/ui/app/routes/csi/volumes/index.js b/ui/app/routes/csi/volumes/index.js index 40aa58f81..e405bb5bd 100644 --- a/ui/app/routes/csi/volumes/index.js +++ b/ui/app/routes/csi/volumes/index.js @@ -3,11 +3,11 @@ import { collect } from '@ember/object/computed'; import { watchQuery } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class IndexRoute extends Route.extend(WithWatchers) { startWatchers(controller) { controller.set('modelWatch', this.watch.perform({ type: 'csi' })); - }, + } - watch: watchQuery('volume'), - watchers: collect('watch'), -}); + @watchQuery('volume') watch; + @collect('watch') watchers; +} diff --git a/ui/app/routes/csi/volumes/volume.js b/ui/app/routes/csi/volumes/volume.js index c4d324fe2..77c2d2d99 100644 --- a/ui/app/routes/csi/volumes/volume.js +++ b/ui/app/routes/csi/volumes/volume.js @@ -6,11 +6,11 @@ import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; import { watchRecord } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { - store: service(), - system: service(), +export default class VolumeRoute extends Route.extend(WithWatchers) { + @service store; + @service system; - breadcrumbs: volume => [ + breadcrumbs = volume => [ { label: 'Volumes', args: [ @@ -26,7 +26,7 @@ export default Route.extend(WithWatchers, { qpBuilder({ volumeNamespace: volume.get('namespace.name') || 'default' }), ], }, - ], + ]; startWatchers(controller, model) { if (!model) return; @@ -34,22 +34,22 @@ export default Route.extend(WithWatchers, { controller.set('watchers', { model: this.watch.perform(model), }); - }, + } serialize(model) { return { volume_name: model.get('plainId') }; - }, + } model(params, transition) { const namespace = transition.to.queryParams.namespace || this.get('system.activeNamespace.id'); const name = params.volume_name; const fullId = JSON.stringify([`csi/${name}`, namespace || 'default']); return this.store.findRecord('volume', fullId, { reload: true }).catch(notifyError(this)); - }, + } // Since volume includes embedded records for allocations, // it's possible that allocations that are server-side deleted may // not be removed from the UI while sitting on the volume detail page. - watch: watchRecord('volume'), - watchers: collect('watch'), -}); + @watchRecord('volume') watch; + @collect('watch') watchers; +} diff --git a/ui/app/routes/exec/task-group/task.js b/ui/app/routes/exec/task-group/task.js index 984bf99d3..39ce0a899 100644 --- a/ui/app/routes/exec/task-group/task.js +++ b/ui/app/routes/exec/task-group/task.js @@ -1,8 +1,8 @@ import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; -export default Route.extend({ - store: service(), +export default class TaskRoute extends Route { + @service store; model({ task_name }) { const allocationQueryParam = this.paramsFor('exec').allocation; @@ -13,7 +13,7 @@ export default Route.extend({ taskName: task_name, taskGroupName, }; - }, + } setupController(controller, { allocationShortId, taskGroupName, taskName }) { this.controllerFor('exec').send('setTaskProperties', { @@ -22,6 +22,6 @@ export default Route.extend({ taskGroupName, }); - this._super(...arguments); - }, -}); + super.setupController(...arguments); + } +} diff --git a/ui/app/routes/index.js b/ui/app/routes/index.js index 7de6fff38..72fc1086e 100644 --- a/ui/app/routes/index.js +++ b/ui/app/routes/index.js @@ -1,7 +1,7 @@ import Route from '@ember/routing/route'; -export default Route.extend({ +export default class IndexRoute extends Route { redirect() { this.transitionTo('jobs'); - }, -}); + } +} diff --git a/ui/app/routes/jobs/index.js b/ui/app/routes/jobs/index.js index 38b9ebe18..c8369e806 100644 --- a/ui/app/routes/jobs/index.js +++ b/ui/app/routes/jobs/index.js @@ -3,11 +3,11 @@ import { collect } from '@ember/object/computed'; import { watchAll } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class IndexRoute extends Route.extend(WithWatchers) { startWatchers(controller) { controller.set('modelWatch', this.watch.perform()); - }, + } - watch: watchAll('job'), - watchers: collect('watch'), -}); + @watchAll('job') watch; + @collect('watch') watchers; +} diff --git a/ui/app/routes/jobs/job.js b/ui/app/routes/jobs/job.js index 70f323049..1cf7d438e 100644 --- a/ui/app/routes/jobs/job.js +++ b/ui/app/routes/jobs/job.js @@ -4,15 +4,15 @@ import RSVP from 'rsvp'; import notifyError from 'nomad-ui/utils/notify-error'; import { jobCrumbs } from 'nomad-ui/utils/breadcrumb-utils'; -export default Route.extend({ - store: service(), - token: service(), +export default class JobRoute extends Route { + @service store; + @service token; - breadcrumbs: jobCrumbs, + breadcrumbs = jobCrumbs; serialize(model) { return { job_name: model.get('plainId') }; - }, + } model(params, transition) { const namespace = transition.to.queryParams.namespace || this.get('system.activeNamespace.id'); @@ -24,5 +24,5 @@ export default Route.extend({ return RSVP.all([job.get('allocations'), job.get('evaluations')]).then(() => job); }) .catch(notifyError(this)); - }, -}); + } +} diff --git a/ui/app/routes/jobs/job/allocations.js b/ui/app/routes/jobs/job/allocations.js index f446ef86a..d5829719c 100644 --- a/ui/app/routes/jobs/job/allocations.js +++ b/ui/app/routes/jobs/job/allocations.js @@ -3,19 +3,19 @@ import { collect } from '@ember/object/computed'; import { watchRelationship } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class AllocationsRoute extends Route.extend(WithWatchers) { model() { const job = this.modelFor('jobs.job'); return job && job.get('allocations').then(() => job); - }, + } startWatchers(controller, model) { if (model) { controller.set('watchAllocations', this.watchAllocations.perform(model)); } - }, + } - watchAllocations: watchRelationship('allocations'), + @watchRelationship('allocations') watchAllocations; - watchers: collect('watchAllocations'), -}); + @collect('watchAllocations') watchers; +} diff --git a/ui/app/routes/jobs/job/definition.js b/ui/app/routes/jobs/job/definition.js index b551bd9b1..4b089e871 100644 --- a/ui/app/routes/jobs/job/definition.js +++ b/ui/app/routes/jobs/job/definition.js @@ -1,6 +1,6 @@ import Route from '@ember/routing/route'; -export default Route.extend({ +export default class DefinitionRoute extends Route { model() { const job = this.modelFor('jobs.job'); if (!job) return; @@ -9,7 +9,7 @@ export default Route.extend({ job, definition, })); - }, + } resetController(controller, isExiting) { if (isExiting) { @@ -18,5 +18,5 @@ export default Route.extend({ job.resetId(); controller.set('isEditing', false); } - }, -}); + } +} diff --git a/ui/app/routes/jobs/job/deployments.js b/ui/app/routes/jobs/job/deployments.js index 699cecf82..e1e9d6b49 100644 --- a/ui/app/routes/jobs/job/deployments.js +++ b/ui/app/routes/jobs/job/deployments.js @@ -4,21 +4,21 @@ import { collect } from '@ember/object/computed'; import { watchRelationship } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class DeploymentsRoute extends Route.extend(WithWatchers) { model() { const job = this.modelFor('jobs.job'); return job && RSVP.all([job.get('deployments'), job.get('versions')]).then(() => job); - }, + } startWatchers(controller, model) { if (model) { controller.set('watchDeployments', this.watchDeployments.perform(model)); controller.set('watchVersions', this.watchVersions.perform(model)); } - }, + } - watchDeployments: watchRelationship('deployments'), - watchVersions: watchRelationship('versions'), + @watchRelationship('deployments') watchDeployments; + @watchRelationship('versions') watchVersions; - watchers: collect('watchDeployments', 'watchVersions'), -}); + @collect('watchDeployments', 'watchVersions') watchers; +} diff --git a/ui/app/routes/jobs/job/evaluations.js b/ui/app/routes/jobs/job/evaluations.js index de811fcc3..cfb7fe1c6 100644 --- a/ui/app/routes/jobs/job/evaluations.js +++ b/ui/app/routes/jobs/job/evaluations.js @@ -3,19 +3,19 @@ import { collect } from '@ember/object/computed'; import { watchRelationship } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class EvaluationsRoute extends Route.extend(WithWatchers) { model() { const job = this.modelFor('jobs.job'); return job && job.get('evaluations').then(() => job); - }, + } startWatchers(controller, model) { if (model) { controller.set('watchEvaluations', this.watchEvaluations.perform(model)); } - }, + } - watchEvaluations: watchRelationship('evaluations'), + @watchRelationship('evaluations') watchEvaluations; - watchers: collect('watchEvaluations'), -}); + @collect('watchEvaluations') watchers; +} diff --git a/ui/app/routes/jobs/job/index.js b/ui/app/routes/jobs/job/index.js index 8e67de5ce..abd8b1d06 100644 --- a/ui/app/routes/jobs/job/index.js +++ b/ui/app/routes/jobs/job/index.js @@ -3,7 +3,7 @@ import { collect } from '@ember/object/computed'; import { watchRecord, watchRelationship, watchAll } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class IndexRoute extends Route.extend(WithWatchers) { startWatchers(controller, model) { if (!model) { return; @@ -17,21 +17,22 @@ export default Route.extend(WithWatchers, { model.get('supportsDeployments') && this.watchLatestDeployment.perform(model), list: model.get('hasChildren') && this.watchAll.perform(), }); - }, + } - watch: watchRecord('job'), - watchAll: watchAll('job'), - watchSummary: watchRecord('job-summary'), - watchAllocations: watchRelationship('allocations'), - watchEvaluations: watchRelationship('evaluations'), - watchLatestDeployment: watchRelationship('latestDeployment'), + @watchRecord('job') watch; + @watchAll('job') watchAll; + @watchRecord('job-summary') watchSummary; + @watchRelationship('allocations') watchAllocations; + @watchRelationship('evaluations') watchEvaluations; + @watchRelationship('latestDeployment') watchLatestDeployment; - watchers: collect( + @collect( 'watch', 'watchAll', 'watchSummary', 'watchAllocations', 'watchEvaluations', 'watchLatestDeployment' - ), -}); + ) + watchers; +} diff --git a/ui/app/routes/jobs/job/task-group.js b/ui/app/routes/jobs/job/task-group.js index 134d7e829..6aad25aa0 100644 --- a/ui/app/routes/jobs/job/task-group.js +++ b/ui/app/routes/jobs/job/task-group.js @@ -7,7 +7,7 @@ import WithWatchers from 'nomad-ui/mixins/with-watchers'; import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; import notifyError from 'nomad-ui/utils/notify-error'; -export default Route.extend(WithWatchers, { +export default class TaskGroupRoute extends Route.extend(WithWatchers) { breadcrumbs(model) { if (!model) return []; return [ @@ -21,7 +21,7 @@ export default Route.extend(WithWatchers, { ], }, ]; - }, + } model({ name }) { const job = this.modelFor('jobs.job'); @@ -49,7 +49,7 @@ export default Route.extend(WithWatchers, { .then(() => taskGroup); }) .catch(notifyError(this)); - }, + } startWatchers(controller, model) { if (model) { @@ -60,11 +60,11 @@ export default Route.extend(WithWatchers, { allocations: this.watchAllocations.perform(job), }); } - }, + } - watchJob: watchRecord('job'), - watchSummary: watchRecord('job-summary'), - watchAllocations: watchRelationship('allocations'), + @watchRecord('job') watchJob; + @watchRecord('job-summary') watchSummary; + @watchRelationship('allocations') watchAllocations; - watchers: collect('watchJob', 'watchSummary', 'watchAllocations'), -}); + @collect('watchJob', 'watchSummary', 'watchAllocations') watchers; +} diff --git a/ui/app/routes/jobs/job/versions.js b/ui/app/routes/jobs/job/versions.js index 38c850eaf..c6ab5684b 100644 --- a/ui/app/routes/jobs/job/versions.js +++ b/ui/app/routes/jobs/job/versions.js @@ -3,18 +3,18 @@ import { collect } from '@ember/object/computed'; import { watchRelationship } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -export default Route.extend(WithWatchers, { +export default class VersionsRoute extends Route.extend(WithWatchers) { model() { const job = this.modelFor('jobs.job'); return job && job.get('versions').then(() => job); - }, + } startWatchers(controller, model) { if (model) { controller.set('watcher', this.watchVersions.perform(model)); } - }, + } - watchVersions: watchRelationship('versions'), - watchers: collect('watchVersions'), -}); + @watchRelationship('versions') watchVersions; + @collect('watchVersions') watchers; +} diff --git a/ui/app/routes/not-found.js b/ui/app/routes/not-found.js index a7df89c01..2c7ece2a2 100644 --- a/ui/app/routes/not-found.js +++ b/ui/app/routes/not-found.js @@ -1,10 +1,10 @@ import Route from '@ember/routing/route'; import EmberError from '@ember/error'; -export default Route.extend({ +export default class NotFoundRoute extends Route { model() { const err = new EmberError('Page not found'); err.code = '404'; this.controllerFor('application').set('error', err); - }, -}); + } +} diff --git a/ui/app/routes/servers/server.js b/ui/app/routes/servers/server.js index 17aa8b10c..ab399d639 100644 --- a/ui/app/routes/servers/server.js +++ b/ui/app/routes/servers/server.js @@ -1,4 +1,4 @@ import Route from '@ember/routing/route'; import WithModelErrorHandling from 'nomad-ui/mixins/with-model-error-handling'; -export default Route.extend(WithModelErrorHandling); +export default class ServerRoute extends Route.extend(WithModelErrorHandling) {} diff --git a/ui/app/serializers/application.js b/ui/app/serializers/application.js index 9546f2972..43247a3a7 100644 --- a/ui/app/serializers/application.js +++ b/ui/app/serializers/application.js @@ -5,19 +5,19 @@ import JSONSerializer from 'ember-data/serializers/json'; import { pluralize, singularize } from 'ember-inflector'; import removeRecord from '../utils/remove-record'; -export default JSONSerializer.extend({ - primaryKey: 'ID', +export default class Application extends JSONSerializer { + primaryKey = 'ID'; keyForAttribute(attr) { return attr.camelize().capitalize(); - }, + } keyForRelationship(attr, relationshipType) { const key = `${singularize(attr) .camelize() .capitalize()}ID`; return relationshipType === 'hasMany' ? pluralize(key) : key; - }, + } // Modeled after the pushPayload for ember-data/serializers/rest pushPayload(store, payload) { @@ -41,13 +41,13 @@ export default JSONSerializer.extend({ }); store.push(documentHash); - }, + } normalizeFindAllResponse(store, modelClass) { - const result = this._super(...arguments); + const result = super.normalizeFindAllResponse(...arguments); this.cullStore(store, modelClass.modelName, result.data); return result; - }, + } // When records are removed server-side, and therefore don't show up in requests, // the local copies of those records need to be unloaded from the store. @@ -65,9 +65,9 @@ export default JSONSerializer.extend({ newRecords.removeObject(newRecord); } }); - }, + } modelNameFromPayloadKey(key) { return singularize(key.dasherize()); - }, -}); + } +} diff --git a/ui/app/serializers/drain-strategy.js b/ui/app/serializers/drain-strategy.js index 033cc45a6..dfaf0c5c8 100644 --- a/ui/app/serializers/drain-strategy.js +++ b/ui/app/serializers/drain-strategy.js @@ -1,6 +1,6 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ +export default class DrainStrategy extends ApplicationSerializer { normalize(typeHash, hash) { // TODO API: finishedAt is always marshaled as a date even when unset. // To simplify things, unset it here when it's the empty date value. @@ -8,6 +8,6 @@ export default ApplicationSerializer.extend({ hash.ForceDeadline = null; } - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/evaluation.js b/ui/app/serializers/evaluation.js index 18e08271e..b4575ba2e 100644 --- a/ui/app/serializers/evaluation.js +++ b/ui/app/serializers/evaluation.js @@ -3,8 +3,8 @@ import { get } from '@ember/object'; import { assign } from '@ember/polyfills'; import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - system: service(), +export default class Evaluation extends ApplicationSerializer { + @service system; normalize(typeHash, hash) { const failures = hash.FailedTGAllocs || {}; @@ -27,6 +27,6 @@ export default ApplicationSerializer.extend({ hash.CreateTimeNanos = hash.CreateTime % 1000000; hash.CreateTime = Math.floor(hash.CreateTime / 1000000); - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/fragment.js b/ui/app/serializers/fragment.js index 838b442f3..435e04210 100644 --- a/ui/app/serializers/fragment.js +++ b/ui/app/serializers/fragment.js @@ -1,3 +1,3 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({}); +export default class Fragment extends ApplicationSerializer {} diff --git a/ui/app/serializers/job-plan.js b/ui/app/serializers/job-plan.js index e44e47d6f..13bd4feb3 100644 --- a/ui/app/serializers/job-plan.js +++ b/ui/app/serializers/job-plan.js @@ -2,13 +2,13 @@ import { assign } from '@ember/polyfills'; import ApplicationSerializer from './application'; import { get } from '@ember/object'; -export default ApplicationSerializer.extend({ +export default class JobPlan extends ApplicationSerializer { normalize(typeHash, hash) { const failures = hash.FailedTGAllocs || {}; hash.FailedTGAllocs = Object.keys(failures).map(key => { return assign({ Name: key }, failures[key] || {}); }); hash.PreemptionIDs = (get(hash, 'Annotations.PreemptedAllocs') || []).mapBy('ID'); - return this._super(...arguments); - }, -}); + return super.normalize(...arguments); + } +} diff --git a/ui/app/serializers/job-summary.js b/ui/app/serializers/job-summary.js index ed464e819..da9c93bb0 100644 --- a/ui/app/serializers/job-summary.js +++ b/ui/app/serializers/job-summary.js @@ -1,7 +1,7 @@ import { get } from '@ember/object'; import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ +export default class JobSummary extends ApplicationSerializer { normalize(modelClass, hash) { // Transform the map-based Summary object into an array-based // TaskGroupSummary fragment list @@ -29,6 +29,6 @@ export default ApplicationSerializer.extend({ ); } - return this._super(modelClass, hash); - }, -}); + return super.normalize(modelClass, hash); + } +} diff --git a/ui/app/serializers/namespace.js b/ui/app/serializers/namespace.js index ccd2350ed..5b35364e8 100644 --- a/ui/app/serializers/namespace.js +++ b/ui/app/serializers/namespace.js @@ -1,5 +1,5 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - primaryKey: 'Name', -}); +export default class Namespace extends ApplicationSerializer { + primaryKey = 'Name'; +} diff --git a/ui/app/serializers/node-attributes.js b/ui/app/serializers/node-attributes.js index 458c81f15..cbd2c5c2e 100644 --- a/ui/app/serializers/node-attributes.js +++ b/ui/app/serializers/node-attributes.js @@ -1,7 +1,7 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ +export default class NodeAttributes extends ApplicationSerializer { normalize(typeHash, hash) { - return this._super(typeHash, { NodeAttributes: hash }); - }, -}); + return super.normalize(typeHash, { NodeAttributes: hash }); + } +} diff --git a/ui/app/serializers/plugin.js b/ui/app/serializers/plugin.js index 6f87159d7..1eed76606 100644 --- a/ui/app/serializers/plugin.js +++ b/ui/app/serializers/plugin.js @@ -12,7 +12,7 @@ const unmap = (hash, propKey) => return record; }); -export default ApplicationSerializer.extend({ +export default class Plugin extends ApplicationSerializer { normalize(typeHash, hash) { hash.PlainId = hash.ID; @@ -28,6 +28,6 @@ export default ApplicationSerializer.extend({ hash.Nodes = unmap(nodes, 'NodeID'); hash.Controllers = unmap(controllers, 'NodeID'); - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/policy.js b/ui/app/serializers/policy.js index b9cfa02ed..a7fd7b1fc 100644 --- a/ui/app/serializers/policy.js +++ b/ui/app/serializers/policy.js @@ -1,8 +1,8 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ +export default class Policy extends ApplicationSerializer { normalize(typeHash, hash) { hash.ID = hash.Name; - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/reschedule-event.js b/ui/app/serializers/reschedule-event.js index 775ddac65..3bbe12904 100644 --- a/ui/app/serializers/reschedule-event.js +++ b/ui/app/serializers/reschedule-event.js @@ -1,6 +1,6 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ +export default class RescheduleEvent extends ApplicationSerializer { normalize(typeHash, hash) { // Time is in the form of nanoseconds since epoch, but JS dates // only understand time to the millisecond precision. So store @@ -12,6 +12,6 @@ export default ApplicationSerializer.extend({ hash.PreviousAllocationId = hash.PrevAllocID ? hash.PrevAllocID : null; hash.PreviousNodeId = hash.PrevNodeID ? hash.PrevNodeID : null; - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/task-group-deployment-summary.js b/ui/app/serializers/task-group-deployment-summary.js index 84c30f323..0441af6be 100644 --- a/ui/app/serializers/task-group-deployment-summary.js +++ b/ui/app/serializers/task-group-deployment-summary.js @@ -1,9 +1,9 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ +export default class TaskGroupDeploymentSummary extends ApplicationSerializer { normalize(typeHash, hash) { hash.PlacedCanaryAllocations = hash.PlacedCanaries || []; delete hash.PlacedCanaries; - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/task-group.js b/ui/app/serializers/task-group.js index 08c5bacd8..d7b0d7c4d 100644 --- a/ui/app/serializers/task-group.js +++ b/ui/app/serializers/task-group.js @@ -1,7 +1,7 @@ import { copy } from 'ember-copy'; import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ +export default class TaskGroup extends ApplicationSerializer { normalize(typeHash, hash) { // Provide EphemeralDisk to each task hash.Tasks.forEach(task => { @@ -14,6 +14,6 @@ export default ApplicationSerializer.extend({ const volumes = hash.Volumes || {}; hash.Volumes = Object.keys(volumes).map(key => volumes[key]); - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/task-state.js b/ui/app/serializers/task-state.js index 0ec3369a1..11a29e25b 100644 --- a/ui/app/serializers/task-state.js +++ b/ui/app/serializers/task-state.js @@ -1,6 +1,6 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ +export default class TaskState extends ApplicationSerializer { normalize(typeHash, hash) { // TODO API: finishedAt is always marshaled as a date even when unset. // To simplify things, unset it here when it's the empty date value. @@ -8,6 +8,6 @@ export default ApplicationSerializer.extend({ hash.FinishedAt = null; } - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/task.js b/ui/app/serializers/task.js index 425b76811..ea09c5dcf 100644 --- a/ui/app/serializers/task.js +++ b/ui/app/serializers/task.js @@ -1,6 +1,6 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ +export default class Task extends ApplicationSerializer { normalize(typeHash, hash) { // Lift the reserved resource numbers out of the Resources object const resources = hash.Resources; @@ -11,6 +11,6 @@ export default ApplicationSerializer.extend({ hash.ReservedEphemeralDisk = hash.EphemeralDisk.SizeMB; } - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/services/breadcrumbs.js b/ui/app/services/breadcrumbs.js index f01a7f680..fd44b9e7a 100644 --- a/ui/app/services/breadcrumbs.js +++ b/ui/app/services/breadcrumbs.js @@ -1,15 +1,18 @@ import { getOwner } from '@ember/application'; import Service, { inject as service } from '@ember/service'; import { computed } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default Service.extend({ - router: service(), +@classic +export default class BreadcrumbsService extends Service { + @service router; // currentURL is only used to listen to all transitions. // currentRouteName has all information necessary to compute breadcrumbs, // but it doesn't change when a transition to the same route with a different // model occurs. - breadcrumbs: computed('router.{currentURL,currentRouteName}', function() { + @computed('router.{currentURL,currentRouteName}') + get breadcrumbs() { const owner = getOwner(this); const allRoutes = (this.get('router.currentRouteName') || '') .split('.') @@ -38,5 +41,5 @@ export default Service.extend({ }); return crumbs; - }), -}); + } +} diff --git a/ui/app/services/config.js b/ui/app/services/config.js index 80e38e1ea..3bed95cc5 100644 --- a/ui/app/services/config.js +++ b/ui/app/services/config.js @@ -3,12 +3,12 @@ import Service from '@ember/service'; import { get } from '@ember/object'; import config from '../config/environment'; -export default Service.extend({ +export default class ConfigService extends Service { unknownProperty(path) { return get(config, path); - }, + } - isDev: equal('environment', 'development'), - isProd: equal('environment', 'production'), - isTest: equal('environment', 'test'), -}); + @equal('environment', 'development') isDev; + @equal('environment', 'production') isProd; + @equal('environment', 'test') isTest; +} diff --git a/ui/app/services/sockets.js b/ui/app/services/sockets.js index b629aa077..29a38b0c7 100644 --- a/ui/app/services/sockets.js +++ b/ui/app/services/sockets.js @@ -2,7 +2,7 @@ import Service from '@ember/service'; import config from 'nomad-ui/config/environment'; import { getOwner } from '@ember/application'; -export default Service.extend({ +export default class SocketsService extends Service { getTaskStateSocket(taskState, command) { const mirageEnabled = config.environment !== 'production' && @@ -34,5 +34,5 @@ export default Service.extend({ `&command=${encodeURIComponent(`["${command}"]`)}` ); } - }, -}); + } +} diff --git a/ui/app/services/stats-trackers-registry.js b/ui/app/services/stats-trackers-registry.js index 751ac1d45..8c2fb2ab7 100644 --- a/ui/app/services/stats-trackers-registry.js +++ b/ui/app/services/stats-trackers-registry.js @@ -14,23 +14,24 @@ let registry; const exists = (tracker, prop) => tracker.get(prop) && !tracker.get(prop).isDestroyed && !tracker.get(prop).isDestroying; -export default Service.extend({ - token: service(), +export default class StatsTrackersRegistryService extends Service { + @service token; - init() { - this._super(...arguments); + constructor() { + super(...arguments); // The LRUMap limits the number of trackers tracked by making room for // new entries beyond the limit by removing the least recently used entry. registry = new LRUMap(MAX_STAT_TRACKERS); - }, + } // A read-only way of getting a reference to the registry. // Since this could be overwritten by a bad actor, it isn't // used in getTracker - registryRef: computed(function() { + @computed + get registryRef() { return registry; - }), + } getTracker(resource) { if (!resource) return; @@ -56,5 +57,5 @@ export default Service.extend({ registry.set(key, tracker); return tracker; - }, -}); + } +} diff --git a/ui/app/services/system.js b/ui/app/services/system.js index bf71e4b3e..3b293d5c4 100644 --- a/ui/app/services/system.js +++ b/ui/app/services/system.js @@ -4,12 +4,15 @@ import PromiseObject from '../utils/classes/promise-object'; import PromiseArray from '../utils/classes/promise-array'; import { namespace } from '../adapters/application'; import jsonWithDefault from '../utils/json-with-default'; +import classic from 'ember-classic-decorator'; -export default Service.extend({ - token: service(), - store: service(), +@classic +export default class SystemService extends Service { + @service token; + @service store; - leader: computed('activeRegion', function() { + @computed('activeRegion') + get leader() { const token = this.token; return PromiseObject.create({ @@ -23,9 +26,10 @@ export default Service.extend({ return leader; }), }); - }), + } - defaultRegion: computed(function() { + @computed + get defaultRegion() { const token = this.token; return PromiseObject.create({ promise: token @@ -35,94 +39,94 @@ export default Service.extend({ return { region: json.ServerRegion }; }), }); - }), + } - regions: computed(function() { + @computed + get regions() { const token = this.token; return PromiseArray.create({ promise: token.authorizedRawRequest(`/${namespace}/regions`).then(jsonWithDefault([])), }); - }), + } - activeRegion: computed('regions.[]', { - get() { - const regions = this.regions; - const region = window.localStorage.nomadActiveRegion; + @computed('regions.[]') + get activeRegion() { + const regions = this.regions; + const region = window.localStorage.nomadActiveRegion; - if (regions.includes(region)) { - return region; - } - - return null; - }, - set(key, value) { - if (value == null) { - window.localStorage.removeItem('nomadActiveRegion'); - return; - } else { - // All localStorage values are strings. Stringify first so - // the return value is consistent with what is persisted. - const strValue = value + ''; - window.localStorage.nomadActiveRegion = strValue; - return strValue; - } - }, - }), - - shouldShowRegions: computed('regions.[]', function() { - return this.get('regions.length') > 1; - }), - - shouldIncludeRegion: computed( - 'activeRegion', - 'defaultRegion.region', - 'shouldShowRegions', - function() { - return this.shouldShowRegions && this.activeRegion !== this.get('defaultRegion.region'); + if (regions.includes(region)) { + return region; } - ), - namespaces: computed('activeRegion', function() { + return null; + } + + set activeRegion(value) { + if (value == null) { + window.localStorage.removeItem('nomadActiveRegion'); + return; + } else { + // All localStorage values are strings. Stringify first so + // the return value is consistent with what is persisted. + const strValue = value + ''; + window.localStorage.nomadActiveRegion = strValue; + return strValue; + } + } + + @computed('regions.[]') + get shouldShowRegions() { + return this.get('regions.length') > 1; + } + + @computed('activeRegion', 'defaultRegion.region', 'shouldShowRegions') + get shouldIncludeRegion() { + return this.shouldShowRegions && this.activeRegion !== this.get('defaultRegion.region'); + } + + @computed('activeRegion') + get namespaces() { return PromiseArray.create({ promise: this.store.findAll('namespace').then(namespaces => namespaces.compact()), }); - }), + } - shouldShowNamespaces: computed('namespaces.[]', function() { + @computed('namespaces.[]') + get shouldShowNamespaces() { const namespaces = this.namespaces.toArray(); return namespaces.length && namespaces.some(namespace => namespace.get('id') !== 'default'); - }), + } - activeNamespace: computed('namespaces.[]', { - get() { - const namespaceId = window.localStorage.nomadActiveNamespace || 'default'; - const namespace = this.namespaces.findBy('id', namespaceId); + @computed('namespaces.[]') + get activeNamespace() { + const namespaceId = window.localStorage.nomadActiveNamespace || 'default'; + const namespace = this.namespaces.findBy('id', namespaceId); - if (namespace) { - return namespace; - } + if (namespace) { + return namespace; + } - // If the namespace in localStorage is no longer in the cluster, it needs to - // be cleared from localStorage + // If the namespace in localStorage is no longer in the cluster, it needs to + // be cleared from localStorage + window.localStorage.removeItem('nomadActiveNamespace'); + return this.namespaces.findBy('id', 'default'); + } + + set activeNamespace(value) { + if (value == null) { window.localStorage.removeItem('nomadActiveNamespace'); - return this.namespaces.findBy('id', 'default'); - }, - set(key, value) { - if (value == null) { - window.localStorage.removeItem('nomadActiveNamespace'); - return; - } else if (typeof value === 'string') { - window.localStorage.nomadActiveNamespace = value; - return this.namespaces.findBy('id', value); - } else { - window.localStorage.nomadActiveNamespace = value.get('name'); - return value; - } - }, - }), + return; + } else if (typeof value === 'string') { + window.localStorage.nomadActiveNamespace = value; + return this.namespaces.findBy('id', value); + } else { + window.localStorage.nomadActiveNamespace = value.get('name'); + return value; + } + } reset() { this.set('activeNamespace', null); - }, -}); + } +} diff --git a/ui/app/services/token.js b/ui/app/services/token.js index 5e6dc70e6..d829431d9 100644 --- a/ui/app/services/token.js +++ b/ui/app/services/token.js @@ -6,29 +6,31 @@ import { assign } from '@ember/polyfills'; import { task } from 'ember-concurrency'; import queryString from 'query-string'; import fetch from 'nomad-ui/utils/fetch'; +import classic from 'ember-classic-decorator'; -export default Service.extend({ - store: service(), - system: service(), +@classic +export default class TokenService extends Service { + @service store; + @service system; - aclEnabled: true, + aclEnabled = true; - secret: computed({ - get() { - return window.localStorage.nomadTokenSecret; - }, - set(key, value) { - if (value == null) { - window.localStorage.removeItem('nomadTokenSecret'); - } else { - window.localStorage.nomadTokenSecret = value; - } + @computed + get secret() { + return window.localStorage.nomadTokenSecret; + } - return value; - }, - }), + set secret(value) { + if (value == null) { + window.localStorage.removeItem('nomadTokenSecret'); + } else { + window.localStorage.nomadTokenSecret = value; + } - fetchSelfToken: task(function*() { + return value; + } + + @task(function*() { const TokenAdapter = getOwner(this).lookup('adapter:token'); try { return yield TokenAdapter.findSelf(); @@ -39,14 +41,16 @@ export default Service.extend({ } return null; } - }), + }) + fetchSelfToken; - selfToken: computed('secret', 'fetchSelfToken.lastSuccessful.value', function() { + @computed('secret', 'fetchSelfToken.lastSuccessful.value') + get selfToken() { if (this.secret) return this.get('fetchSelfToken.lastSuccessful.value'); return; - }), + } - fetchSelfTokenPolicies: task(function*() { + @task(function*() { try { if (this.selfToken) { return yield this.selfToken.get('policies'); @@ -57,16 +61,18 @@ export default Service.extend({ } catch (e) { return []; } - }), + }) + fetchSelfTokenPolicies; - selfTokenPolicies: alias('fetchSelfTokenPolicies.lastSuccessful.value'), + @alias('fetchSelfTokenPolicies.lastSuccessful.value') selfTokenPolicies; - fetchSelfTokenAndPolicies: task(function*() { + @task(function*() { yield this.fetchSelfToken.perform(); if (this.aclEnabled) { yield this.fetchSelfTokenPolicies.perform(); } - }), + }) + fetchSelfTokenAndPolicies; // All non Ember Data requests should go through authorizedRequest. // However, the request that gets regions falls into that category. @@ -83,7 +89,7 @@ export default Service.extend({ } return fetch(url, assign(options, { headers, credentials })); - }, + } authorizedRequest(url, options) { if (this.get('system.shouldIncludeRegion')) { @@ -94,14 +100,14 @@ export default Service.extend({ } return this.authorizedRawRequest(url, options); - }, + } reset() { this.fetchSelfToken.cancelAll({ resetState: true }); this.fetchSelfTokenPolicies.cancelAll({ resetState: true }); this.fetchSelfTokenAndPolicies.cancelAll({ resetState: true }); - }, -}); + } +} function addParams(url, params) { const paramsStr = queryString.stringify(params); diff --git a/ui/app/services/user-settings.js b/ui/app/services/user-settings.js index f9841ad57..e551dd98b 100644 --- a/ui/app/services/user-settings.js +++ b/ui/app/services/user-settings.js @@ -1,6 +1,6 @@ import Service from '@ember/service'; import localStorageProperty from 'nomad-ui/utils/properties/local-storage'; -export default Service.extend({ - pageSize: localStorageProperty('nomadPageSize', 25), -}); +export default class UserSettingsService extends Service { + @localStorageProperty('nomadPageSize', 25) pageSize; +} diff --git a/ui/app/services/watch-list.js b/ui/app/services/watch-list.js index ab068650d..b22fc97e8 100644 --- a/ui/app/services/watch-list.js +++ b/ui/app/services/watch-list.js @@ -5,23 +5,24 @@ import Service from '@ember/service'; let list = {}; -export default Service.extend({ - _list: computed(function() { +export default class WatchListService extends Service { + @computed + get _list() { return copy(list, true); - }), + } - list: readOnly('_list'), + @readOnly('_list') list; - init() { - this._super(...arguments); + constructor() { + super(...arguments); list = {}; - }, + } getIndexFor(url) { return list[url] || 1; - }, + } setIndexFor(url, value) { list[url] = +value; - }, -}); + } +} diff --git a/ui/app/utils/classes/allocation-stats-tracker.js b/ui/app/utils/classes/allocation-stats-tracker.js index 87da60094..6e428fcde 100644 --- a/ui/app/utils/classes/allocation-stats-tracker.js +++ b/ui/app/utils/classes/allocation-stats-tracker.js @@ -1,7 +1,8 @@ -import EmberObject, { computed, get } from '@ember/object'; +import EmberObject, { get, computed } from '@ember/object'; import { alias } from '@ember/object/computed'; import RollingArray from 'nomad-ui/utils/classes/rolling-array'; import AbstractStatsTracker from 'nomad-ui/utils/classes/abstract-stats-tracker'; +import classic from 'ember-classic-decorator'; const percent = (numerator, denominator) => { if (!numerator || !denominator) { @@ -12,13 +13,15 @@ const percent = (numerator, denominator) => { const empty = ts => ({ timestamp: ts, used: null, percent: null }); -const AllocationStatsTracker = EmberObject.extend(AbstractStatsTracker, { +@classic +class AllocationStatsTracker extends EmberObject.extend(AbstractStatsTracker) { // Set via the stats computed property macro - allocation: null, + allocation = null; - url: computed('allocation', function() { + @computed('allocation') + get url() { return `/v1/client/allocation/${this.get('allocation.id')}/stats`; - }), + } append(frame) { const timestamp = new Date(Math.floor(frame.Timestamp / 1000000)); @@ -61,7 +64,7 @@ const AllocationStatsTracker = EmberObject.extend(AbstractStatsTracker, { percent: percent(taskMemoryUsed / 1024 / 1024, stats.reservedMemory), }); } - }, + } pause() { const ts = new Date(); @@ -71,22 +74,26 @@ const AllocationStatsTracker = EmberObject.extend(AbstractStatsTracker, { task.memory.pushObject(empty(ts)); task.cpu.pushObject(empty(ts)); }); - }, + } // Static figures, denominators for stats - reservedCPU: alias('allocation.taskGroup.reservedCPU'), - reservedMemory: alias('allocation.taskGroup.reservedMemory'), + @alias('allocation.taskGroup.reservedCPU') reservedCPU; + @alias('allocation.taskGroup.reservedMemory') reservedMemory; // Dynamic figures, collected over time // []{ timestamp: Date, used: Number, percent: Number } - cpu: computed('allocation', function() { + @computed('allocation') + get cpu() { return RollingArray(this.bufferSize); - }), - memory: computed('allocation', function() { - return RollingArray(this.bufferSize); - }), + } - tasks: computed('allocation', function() { + @computed('allocation') + get memory() { + return RollingArray(this.bufferSize); + } + + @computed('allocation') + get tasks() { const bufferSize = this.bufferSize; const tasks = this.get('allocation.taskGroup.tasks') || []; return tasks.map(task => ({ @@ -101,8 +108,8 @@ const AllocationStatsTracker = EmberObject.extend(AbstractStatsTracker, { cpu: RollingArray(bufferSize), memory: RollingArray(bufferSize), })); - }), -}); + } +} export default AllocationStatsTracker; diff --git a/ui/app/utils/classes/log.js b/ui/app/utils/classes/log.js index c1ebb6763..b23d14704 100644 --- a/ui/app/utils/classes/log.js +++ b/ui/app/utils/classes/log.js @@ -11,45 +11,54 @@ import StreamLogger from 'nomad-ui/utils/classes/stream-logger'; import PollLogger from 'nomad-ui/utils/classes/poll-logger'; import { decode } from 'nomad-ui/utils/stream-frames'; import Anser from 'anser'; +import classic from 'ember-classic-decorator'; const MAX_OUTPUT_LENGTH = 50000; // eslint-disable-next-line export const fetchFailure = url => () => console.warn(`LOG FETCH: Couldn't connect to ${url}`); -const Log = EmberObject.extend(Evented, { +@classic +class Log extends EmberObject.extend(Evented) { // Parameters - url: '', - params: overridable(() => ({})), - plainText: false, + url = ''; + + @overridable(() => ({})) + params; + + plainText = false; + logFetch() { assert('Log objects need a logFetch method, which should have an interface like window.fetch'); - }, + } // Read-only state - isStreaming: alias('logStreamer.poll.isRunning'), - logPointer: null, - logStreamer: null, + @alias('logStreamer.poll.isRunning') + isStreaming; + + logPointer = null; + logStreamer = null; // The top of the log - head: '', + head = ''; // The bottom of the log - tail: '', + tail = ''; // The top or bottom of the log, depending on whether // the logPointer is pointed at head or tail - output: computed('logPointer', 'head', 'tail', function() { + @computed('logPointer', 'head', 'tail') + get output() { let logs = this.logPointer === 'head' ? this.head : this.tail; logs = logs.replace(//g, '>'); let colouredLogs = Anser.ansiToHtml(logs); return htmlSafe(colouredLogs); - }), + } init() { - this._super(); + super.init(); const args = this.getProperties('url', 'params', 'logFetch'); args.write = chunk => { @@ -66,14 +75,14 @@ const Log = EmberObject.extend(Evented, { } else { this.set('logStreamer', PollLogger.create(args)); } - }, + } destroy() { this.stop(); - this._super(); - }, + super.destroy(); + } - gotoHead: task(function*() { + @task(function*() { const logFetch = this.logFetch; const queryParams = queryString.stringify( assign( @@ -96,9 +105,10 @@ const Log = EmberObject.extend(Evented, { } this.set('head', text); this.set('logPointer', 'head'); - }), + }) + gotoHead; - gotoTail: task(function*() { + @task(function*() { const logFetch = this.logFetch; const queryParams = queryString.stringify( assign( @@ -117,17 +127,18 @@ const Log = EmberObject.extend(Evented, { this.set('tail', text); this.set('logPointer', 'tail'); - }), + }) + gotoTail; startStreaming() { this.set('logPointer', 'tail'); return this.logStreamer.start(); - }, + } stop() { this.logStreamer.stop(); - }, -}); + } +} export default Log; diff --git a/ui/app/utils/classes/node-stats-tracker.js b/ui/app/utils/classes/node-stats-tracker.js index 41312fc87..f589a8a81 100644 --- a/ui/app/utils/classes/node-stats-tracker.js +++ b/ui/app/utils/classes/node-stats-tracker.js @@ -2,6 +2,7 @@ import EmberObject, { computed } from '@ember/object'; import { alias } from '@ember/object/computed'; import RollingArray from 'nomad-ui/utils/classes/rolling-array'; import AbstractStatsTracker from 'nomad-ui/utils/classes/abstract-stats-tracker'; +import classic from 'ember-classic-decorator'; const percent = (numerator, denominator) => { if (!numerator || !denominator) { @@ -12,13 +13,15 @@ const percent = (numerator, denominator) => { const empty = ts => ({ timestamp: ts, used: null, percent: null }); -const NodeStatsTracker = EmberObject.extend(AbstractStatsTracker, { +@classic +class NodeStatsTracker extends EmberObject.extend(AbstractStatsTracker) { // Set via the stats computed property macro - node: null, + node = null; - url: computed('node', function() { + @computed('node') + get url() { return `/v1/client/stats?node_id=${this.get('node.id')}`; - }), + } append(frame) { const timestamp = new Date(Math.floor(frame.Timestamp / 1000000)); @@ -36,27 +39,30 @@ const NodeStatsTracker = EmberObject.extend(AbstractStatsTracker, { used: memoryUsed, percent: percent(memoryUsed / 1024 / 1024, this.reservedMemory), }); - }, + } pause() { const ts = new Date(); this.memory.pushObject(empty(ts)); this.cpu.pushObject(empty(ts)); - }, + } // Static figures, denominators for stats - reservedCPU: alias('node.resources.cpu'), - reservedMemory: alias('node.resources.memory'), + @alias('node.resources.cpu') reservedCPU; + @alias('node.resources.memory') reservedMemory; // Dynamic figures, collected over time // []{ timestamp: Date, used: Number, percent: Number } - cpu: computed('node', function() { + @computed('node') + get cpu() { return RollingArray(this.bufferSize); - }), - memory: computed('node', function() { + } + + @computed('node') + get memory() { return RollingArray(this.bufferSize); - }), -}); + } +} export default NodeStatsTracker; diff --git a/ui/app/utils/classes/poll-logger.js b/ui/app/utils/classes/poll-logger.js index ce42fb8dc..6fe015c2e 100644 --- a/ui/app/utils/classes/poll-logger.js +++ b/ui/app/utils/classes/poll-logger.js @@ -3,19 +3,21 @@ import { task, timeout } from 'ember-concurrency'; import { decode } from 'nomad-ui/utils/stream-frames'; import AbstractLogger from './abstract-logger'; import { fetchFailure } from './log'; +import classic from 'ember-classic-decorator'; -export default EmberObject.extend(AbstractLogger, { - interval: 1000, +@classic +export default class PollLogger extends EmberObject.extend(AbstractLogger) { + interval = 1000; start() { return this.poll.linked().perform(); - }, + } stop() { return this.poll.cancelAll(); - }, + } - poll: task(function*() { + @task(function*() { const { interval, logFetch } = this; while (true) { const url = this.fullUrl; @@ -37,5 +39,6 @@ export default EmberObject.extend(AbstractLogger, { yield timeout(interval); } - }), -}); + }) + poll; +} diff --git a/ui/app/utils/classes/promise-array.js b/ui/app/utils/classes/promise-array.js index 0fd302aec..1bf2efb1a 100644 --- a/ui/app/utils/classes/promise-array.js +++ b/ui/app/utils/classes/promise-array.js @@ -1,4 +1,6 @@ import ArrayProxy from '@ember/array/proxy'; import PromiseProxyMixin from '@ember/object/promise-proxy-mixin'; +import classic from 'ember-classic-decorator'; -export default ArrayProxy.extend(PromiseProxyMixin); +@classic +export default class PromiseArray extends ArrayProxy.extend(PromiseProxyMixin) {} diff --git a/ui/app/utils/classes/promise-object.js b/ui/app/utils/classes/promise-object.js index fd762b728..dbab8f7ec 100644 --- a/ui/app/utils/classes/promise-object.js +++ b/ui/app/utils/classes/promise-object.js @@ -1,4 +1,6 @@ import ObjectProxy from '@ember/object/proxy'; import PromiseProxyMixin from '@ember/object/promise-proxy-mixin'; +import classic from 'ember-classic-decorator'; -export default ObjectProxy.extend(PromiseProxyMixin); +@classic +export default class PromiseObject extends ObjectProxy.extend(PromiseProxyMixin) {} diff --git a/ui/app/utils/classes/query-params.js b/ui/app/utils/classes/query-params.js index 4f4efc08d..9c61582d4 100644 --- a/ui/app/utils/classes/query-params.js +++ b/ui/app/utils/classes/query-params.js @@ -1,11 +1,13 @@ // Copied from source since it isn't exposed to import // https://github.com/emberjs/ember.js/blob/v2.18.2/packages/ember-routing/lib/system/query_params.js import EmberObject from '@ember/object'; +import classic from 'ember-classic-decorator'; -const QueryParams = EmberObject.extend({ - isQueryParams: true, - values: null, -}); +@classic +class QueryParams extends EmberObject { + isQueryParams = true; + values = null; +} export const qpBuilder = values => QueryParams.create({ values }); diff --git a/ui/package.json b/ui/package.json index 19488169e..b4158b09f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -53,6 +53,7 @@ "ember-ajax": "^5.0.0", "ember-auto-import": "^1.2.21", "ember-can": "^2.0.0", + "ember-classic-decorator": "^1.0.8", "ember-cli": "~3.12.0", "ember-cli-babel": "^7.7.3", "ember-cli-clipboard": "^0.13.0", @@ -76,6 +77,7 @@ "ember-copy": "^1.0.0", "ember-data": "~3.12.0", "ember-data-model-fragments": "4.0.0", + "ember-decorators": "^6.1.1", "ember-export-application-global": "^2.0.0", "ember-fetch": "^6.5.0", "ember-inflector": "^3.0.0", diff --git a/ui/yarn.lock b/ui/yarn.lock index b3a5edbe5..f90cf1b36 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -16,12 +16,21 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/code-frame@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== +"@babel/code-frame@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" + integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== dependencies: - "@babel/highlight" "^7.8.3" + "@babel/highlight" "^7.10.1" + +"@babel/compat-data@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db" + integrity sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw== + dependencies: + browserslist "^4.12.0" + invariant "^2.2.4" + semver "^5.5.0" "@babel/core@^7.0.0", "@babel/core@^7.1.6", "@babel/core@^7.3.3", "@babel/core@^7.3.4": version "7.4.0" @@ -83,6 +92,38 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.8.3", "@babel/core@^7.9.0": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a" + integrity sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ== + dependencies: + "@babel/code-frame" "^7.10.1" + "@babel/generator" "^7.10.2" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helpers" "^7.10.1" + "@babel/parser" "^7.10.2" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.2" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.10.1", "@babel/generator@^7.10.2": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9" + integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA== + dependencies: + "@babel/types" "^7.10.2" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + "@babel/generator@^7.4.0", "@babel/generator@^7.6.2": version "7.6.2" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.2.tgz#dac8a3c2df118334c2a29ff3446da1636a8f8c03" @@ -104,16 +145,6 @@ source-map "^0.5.0" trim-right "^1.0.1" -"@babel/generator@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" - integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ== - dependencies: - "@babel/types" "^7.9.6" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" - "@babel/helper-annotate-as-pure@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" @@ -121,6 +152,13 @@ dependencies: "@babel/types" "^7.0.0" +"@babel/helper-annotate-as-pure@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz#f6d08acc6f70bbd59b436262553fb2e259a1a268" + integrity sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw== + dependencies: + "@babel/types" "^7.10.1" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f" @@ -129,6 +167,14 @@ "@babel/helper-explode-assignable-expression" "^7.1.0" "@babel/types" "^7.0.0" +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz#0ec7d9be8174934532661f87783eb18d72290059" + integrity sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.10.1" + "@babel/types" "^7.10.1" + "@babel/helper-call-delegate@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43" @@ -138,6 +184,29 @@ "@babel/traverse" "^7.4.4" "@babel/types" "^7.4.4" +"@babel/helper-compilation-targets@^7.10.2", "@babel/helper-compilation-targets@^7.8.7": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz#a17d9723b6e2c750299d2a14d4637c76936d8285" + integrity sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA== + dependencies: + "@babel/compat-data" "^7.10.1" + browserslist "^4.12.0" + invariant "^2.2.4" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/helper-create-class-features-plugin@^7.10.1": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz#7474295770f217dbcf288bf7572eb213db46ee67" + integrity sha512-5C/QhkGFh1vqcziq1vAL6SI9ymzUp8BCYjFpvYVhWP4DlATIb3u5q3iUd35mvlyGs8fO7hckkW7i0tmH+5+bvQ== + dependencies: + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-member-expression-to-functions" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/helper-create-class-features-plugin@^7.4.0", "@babel/helper-create-class-features-plugin@^7.5.5": version "7.6.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.6.0.tgz#769711acca889be371e9bc2eb68641d55218021f" @@ -162,6 +231,24 @@ "@babel/helper-replace-supers" "^7.4.4" "@babel/helper-split-export-declaration" "^7.4.4" +"@babel/helper-create-regexp-features-plugin@^7.10.1", "@babel/helper-create-regexp-features-plugin@^7.8.3": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz#1b8feeab1594cbcfbf3ab5a3bbcabac0468efdbd" + integrity sha512-Rx4rHS0pVuJn5pJOqaqcZR4XSgeF9G/pO/79t+4r7380tXFJdzImFnxMU19f83wjSrmKHq6myrM10pFHTGzkUA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-regex" "^7.10.1" + regexpu-core "^4.7.0" + +"@babel/helper-define-map@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz#5e69ee8308648470dd7900d159c044c10285221d" + integrity sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg== + dependencies: + "@babel/helper-function-name" "^7.10.1" + "@babel/types" "^7.10.1" + lodash "^4.17.13" + "@babel/helper-define-map@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz#6969d1f570b46bdc900d1eba8e5d59c48ba2c12a" @@ -188,6 +275,14 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" +"@babel/helper-explode-assignable-expression@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz#e9d76305ee1162ca467357ae25df94f179af2b7e" + integrity sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg== + dependencies: + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" + "@babel/helper-function-name@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" @@ -197,14 +292,14 @@ "@babel/template" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-function-name@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" - integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== +"@babel/helper-function-name@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4" + integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.9.5" + "@babel/helper-get-function-arity" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" "@babel/helper-get-function-arity@^7.0.0": version "7.0.0" @@ -213,12 +308,19 @@ dependencies: "@babel/types" "^7.0.0" -"@babel/helper-get-function-arity@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" - integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== +"@babel/helper-get-function-arity@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d" + integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" + +"@babel/helper-hoist-variables@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz#7e77c82e5dcae1ebf123174c385aaadbf787d077" + integrity sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg== + dependencies: + "@babel/types" "^7.10.1" "@babel/helper-hoist-variables@^7.4.4": version "7.4.4" @@ -234,6 +336,13 @@ dependencies: "@babel/types" "^7.0.0" +"@babel/helper-member-expression-to-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15" + integrity sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g== + dependencies: + "@babel/types" "^7.10.1" + "@babel/helper-member-expression-to-functions@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590" @@ -248,6 +357,13 @@ dependencies: "@babel/types" "^7.0.0" +"@babel/helper-module-imports@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876" + integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg== + dependencies: + "@babel/types" "^7.10.1" + "@babel/helper-module-transforms@^7.1.0": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz#96115ea42a2f139e619e98ed46df6019b94414b8" @@ -260,6 +376,19 @@ "@babel/types" "^7.4.4" lodash "^4.17.11" +"@babel/helper-module-transforms@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622" + integrity sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg== + dependencies: + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-simple-access" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" + lodash "^4.17.13" + "@babel/helper-module-transforms@^7.4.4": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a" @@ -279,11 +408,23 @@ dependencies: "@babel/types" "^7.0.0" +"@babel/helper-optimise-call-expression@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543" + integrity sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg== + dependencies: + "@babel/types" "^7.10.1" + "@babel/helper-plugin-utils@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== +"@babel/helper-plugin-utils@^7.10.1", "@babel/helper-plugin-utils@^7.8.0": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127" + integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA== + "@babel/helper-regex@^7.0.0": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.4.4.tgz#a47e02bc91fb259d2e6727c2a30013e3ac13c4a2" @@ -291,6 +432,13 @@ dependencies: lodash "^4.17.11" +"@babel/helper-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.1.tgz#021cf1a7ba99822f993222a001cc3fec83255b96" + integrity sha512-7isHr19RsIJWWLLFn21ubFt223PjQyg1HY7CZEMRr820HttHPpVvrsIN3bUOo44DEfFV4kBXO7Abbn9KTUZV7g== + dependencies: + lodash "^4.17.13" + "@babel/helper-regex@^7.4.4": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" @@ -309,6 +457,17 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" +"@babel/helper-remap-async-to-generator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz#bad6aaa4ff39ce8d4b82ccaae0bfe0f7dbb5f432" + integrity sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-wrap-function" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" + "@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz#aee41783ebe4f2d3ab3ae775e1cc6f1a90cefa27" @@ -319,6 +478,16 @@ "@babel/traverse" "^7.4.4" "@babel/types" "^7.4.4" +"@babel/helper-replace-supers@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" + integrity sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" + "@babel/helper-replace-supers@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2" @@ -337,6 +506,21 @@ "@babel/template" "^7.1.0" "@babel/types" "^7.0.0" +"@babel/helper-simple-access@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" + integrity sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw== + dependencies: + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" + +"@babel/helper-split-export-declaration@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f" + integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g== + dependencies: + "@babel/types" "^7.10.1" + "@babel/helper-split-export-declaration@^7.4.0", "@babel/helper-split-export-declaration@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" @@ -344,17 +528,10 @@ dependencies: "@babel/types" "^7.4.4" -"@babel/helper-split-export-declaration@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" - integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" - integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== +"@babel/helper-validator-identifier@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" + integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== "@babel/helper-wrap-function@^7.1.0": version "7.2.0" @@ -366,6 +543,25 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.2.0" +"@babel/helper-wrap-function@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz#956d1310d6696257a7afd47e4c42dfda5dfcedc9" + integrity sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ== + dependencies: + "@babel/helper-function-name" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" + +"@babel/helpers@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973" + integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw== + dependencies: + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" + "@babel/helpers@^7.4.0", "@babel/helpers@^7.6.2": version "7.6.2" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.6.2.tgz#681ffe489ea4dcc55f23ce469e58e59c1c045153" @@ -393,15 +589,20 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/highlight@^7.8.3": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" - integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== +"@babel/highlight@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" + integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== dependencies: - "@babel/helper-validator-identifier" "^7.9.0" + "@babel/helper-validator-identifier" "^7.10.1" chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/parser@^7.10.1", "@babel/parser@^7.10.2", "@babel/parser@^7.7.0": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" + integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== + "@babel/parser@^7.3.4", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" @@ -412,10 +613,14 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.2.tgz#205e9c95e16ba3b8b96090677a67c9d6075b70a1" integrity sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg== -"@babel/parser@^7.7.0", "@babel/parser@^7.8.6", "@babel/parser@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" - integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== +"@babel/plugin-proposal-async-generator-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz#6911af5ba2e615c4ff3c497fe2f47b35bf6d7e55" + integrity sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-remap-async-to-generator" "^7.10.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" "@babel/plugin-proposal-async-generator-functions@^7.2.0": version "7.2.0" @@ -434,6 +639,14 @@ "@babel/helper-create-class-features-plugin" "^7.5.0" "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-proposal-class-properties@^7.10.1", "@babel/plugin-proposal-class-properties@^7.8.3": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.1.tgz#046bc7f6550bb08d9bd1d4f060f5f5a4f1087e01" + integrity sha512-sqdGWgoXlnOdgMXU+9MbhzwFRgxVLeiGBqTrnuS7LC2IBU31wSsESbTUreT2O418obpfPdGUR2GbEufZF1bpqw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-proposal-class-properties@^7.3.3": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4" @@ -459,6 +672,23 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-decorators" "^7.2.0" +"@babel/plugin-proposal-decorators@^7.8.3": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.1.tgz#9373c2d8db45345c6e30452ad77b469758e5c8f7" + integrity sha512-xBfteh352MTke2U1NpclzMDmAmCdQ2fBZjhZQQfGTjXw6qcRYMkt528sA1U8o0ThDCSeuETXIj5bOGdxN+5gkw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-decorators" "^7.10.1" + +"@babel/plugin-proposal-dynamic-import@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz#e36979dc1dc3b73f6d6816fc4951da2363488ef0" + integrity sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-proposal-dynamic-import@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz#e532202db4838723691b10a67b8ce509e397c506" @@ -467,6 +697,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-dynamic-import" "^7.2.0" +"@babel/plugin-proposal-json-strings@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz#b1e691ee24c651b5a5e32213222b2379734aff09" + integrity sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-proposal-json-strings@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" @@ -475,6 +713,31 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-json-strings" "^7.2.0" +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz#02dca21673842ff2fe763ac253777f235e9bbf78" + integrity sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + +"@babel/plugin-proposal-numeric-separator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz#a9a38bc34f78bdfd981e791c27c6fdcec478c123" + integrity sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-numeric-separator" "^7.10.1" + +"@babel/plugin-proposal-object-rest-spread@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz#cba44908ac9f142650b4a65b8aa06bf3478d5fb6" + integrity sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.10.1" + "@babel/plugin-proposal-object-rest-spread@^7.3.2", "@babel/plugin-proposal-object-rest-spread@^7.6.2": version "7.6.2" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.6.2.tgz#8ffccc8f3a6545e9f78988b6bf4fe881b88e8096" @@ -499,6 +762,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.2.0" +"@babel/plugin-proposal-optional-catch-binding@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz#c9f86d99305f9fa531b568ff5ab8c964b8b223d2" + integrity sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-proposal-optional-catch-binding@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" @@ -507,6 +778,30 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" +"@babel/plugin-proposal-optional-chaining@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz#15f5d6d22708629451a91be28f8facc55b0e818c" + integrity sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-proposal-private-methods@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz#ed85e8058ab0fe309c3f448e5e1b73ca89cdb598" + integrity sha512-RZecFFJjDiQ2z6maFprLgrdnm0OzoC23Mx89xf1CcEsxmHuzuXOdniEuI+S3v7vjQG4F5sa6YtUp+19sZuSxHg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-proposal-unicode-property-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f" + integrity sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78" @@ -532,6 +827,27 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-async-generators@^7.8.0": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz#d5bc0645913df5b17ad7eda0fa2308330bde34c5" + integrity sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-syntax-decorators@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.1.tgz#16b869c4beafc9a442565147bda7ce0967bd4f13" + integrity sha512-a9OAbQhKOwSle1Vr0NJu/ISg1sPfdEkfRKWpgPuzhnWWzForou2gIeUIIwjAMHRekhhpJ7eulZlYs0H14Cbi+g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-decorators@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz#c50b1b957dcc69e4b1127b65e1c33eef61570c1b" @@ -546,6 +862,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-dynamic-import@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + "@babel/plugin-syntax-json-strings@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470" @@ -553,6 +876,27 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-json-strings@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz#25761ee7410bc8cf97327ba741ee94e4a61b7d99" + integrity sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-object-rest-spread@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" @@ -560,6 +904,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" @@ -567,6 +918,34 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-optional-catch-binding@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz#8b8733f8c57397b3eaa47ddba8841586dcaef362" + integrity sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-syntax-typescript@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.1.tgz#5e82bc27bb4202b93b949b029e699db536733810" + integrity sha512-X/d8glkrAtra7CaQGMiGs/OGa6XgUzqPcBXCIGFCpCqnfGlT0Wfbzo/B89xHhnInTaItPK8LALblVXcUOEh95Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-typescript@^7.2.0": version "7.3.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991" @@ -574,6 +953,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-arrow-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz#cb5ee3a36f0863c06ead0b409b4cc43a889b295b" + integrity sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-arrow-functions@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550" @@ -581,6 +967,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-async-to-generator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz#e5153eb1a3e028f79194ed8a7a4bf55f862b2062" + integrity sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg== + dependencies: + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-remap-async-to-generator" "^7.10.1" + "@babel/plugin-transform-async-to-generator@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e" @@ -590,6 +985,13 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-remap-async-to-generator" "^7.1.0" +"@babel/plugin-transform-block-scoped-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz#146856e756d54b20fff14b819456b3e01820b85d" + integrity sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-block-scoped-functions@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" @@ -597,6 +999,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-block-scoping@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz#47092d89ca345811451cd0dc5d91605982705d5e" + integrity sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + lodash "^4.17.13" + "@babel/plugin-transform-block-scoping@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz#c13279fabf6b916661531841a23c4b7dae29646d" @@ -613,6 +1023,20 @@ "@babel/helper-plugin-utils" "^7.0.0" lodash "^4.17.13" +"@babel/plugin-transform-classes@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz#6e11dd6c4dfae70f540480a4702477ed766d733f" + integrity sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-define-map" "^7.10.1" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + globals "^11.1.0" + "@babel/plugin-transform-classes@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz#0ce4094cdafd709721076d3b9c38ad31ca715eb6" @@ -641,6 +1065,13 @@ "@babel/helper-split-export-declaration" "^7.4.4" globals "^11.1.0" +"@babel/plugin-transform-computed-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz#59aa399064429d64dce5cf76ef9b90b7245ebd07" + integrity sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-computed-properties@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da" @@ -648,6 +1079,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-destructuring@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz#abd58e51337815ca3a22a336b85f62b998e71907" + integrity sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-destructuring@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a" @@ -662,6 +1100,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-dotall-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz#920b9fec2d78bb57ebb64a644d5c2ba67cc104ee" + integrity sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3" @@ -680,6 +1126,13 @@ "@babel/helper-regex" "^7.4.4" regexpu-core "^4.6.0" +"@babel/plugin-transform-duplicate-keys@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz#c900a793beb096bc9d4d0a9d0cde19518ffc83b9" + integrity sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-duplicate-keys@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853" @@ -687,6 +1140,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-exponentiation-operator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz#279c3116756a60dd6e6f5e488ba7957db9c59eb3" + integrity sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-exponentiation-operator@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" @@ -695,6 +1156,13 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-for-of@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz#ff01119784eb0ee32258e8646157ba2501fcfda5" + integrity sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-for-of@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556" @@ -702,6 +1170,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-function-name@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz#4ed46fd6e1d8fde2a2ec7b03c66d853d2c92427d" + integrity sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw== + dependencies: + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-function-name@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad" @@ -710,6 +1186,13 @@ "@babel/helper-function-name" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz#5794f8da82846b22e4e6631ea1658bce708eb46a" + integrity sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-literals@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1" @@ -717,6 +1200,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-member-expression-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz#90347cba31bca6f394b3f7bd95d2bbfd9fce2f39" + integrity sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-member-expression-literals@^7.2.0", "@babel/plugin-transform-modules-amd@^7.0.0", "@babel/plugin-transform-modules-amd@^7.2.0": name "@babel/plugin-transform-member-expression-literals" version "7.2.0" @@ -725,6 +1215,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-modules-amd@^7.10.1", "@babel/plugin-transform-modules-amd@^7.9.0": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz#65950e8e05797ebd2fe532b96e19fc5482a1d52a" + integrity sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw== + dependencies: + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-amd@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91" @@ -734,6 +1233,16 @@ "@babel/helper-plugin-utils" "^7.0.0" babel-plugin-dynamic-import-node "^2.3.0" +"@babel/plugin-transform-modules-commonjs@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz#d5ff4b4413ed97ffded99961056e1fb980fb9301" + integrity sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg== + dependencies: + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-simple-access" "^7.10.1" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-commonjs@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74" @@ -754,6 +1263,16 @@ "@babel/helper-simple-access" "^7.1.0" babel-plugin-dynamic-import-node "^2.3.0" +"@babel/plugin-transform-modules-systemjs@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz#9962e4b0ac6aaf2e20431ada3d8ec72082cbffb6" + integrity sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA== + dependencies: + "@babel/helper-hoist-variables" "^7.10.1" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-systemjs@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249" @@ -763,6 +1282,14 @@ "@babel/helper-plugin-utils" "^7.0.0" babel-plugin-dynamic-import-node "^2.3.0" +"@babel/plugin-transform-modules-umd@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz#ea080911ffc6eb21840a5197a39ede4ee67b1595" + integrity sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA== + dependencies: + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-modules-umd@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae" @@ -785,6 +1312,20 @@ dependencies: regexpu-core "^4.6.0" +"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" + integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + +"@babel/plugin-transform-new-target@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz#6ee41a5e648da7632e22b6fb54012e87f612f324" + integrity sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-new-target@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5" @@ -792,6 +1333,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-object-super@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz#2e3016b0adbf262983bf0d5121d676a5ed9c4fde" + integrity sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/plugin-transform-object-super@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz#b35d4c10f56bab5d650047dad0f1d8e8814b6598" @@ -808,6 +1357,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-replace-supers" "^7.5.5" +"@babel/plugin-transform-parameters@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz#b25938a3c5fae0354144a720b07b32766f683ddd" + integrity sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg== + dependencies: + "@babel/helper-get-function-arity" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-parameters@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16" @@ -817,6 +1374,13 @@ "@babel/helper-get-function-arity" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-property-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz#cffc7315219230ed81dc53e4625bf86815b6050d" + integrity sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-property-literals@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905" @@ -832,6 +1396,13 @@ "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-regenerator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz#10e175cbe7bdb63cc9b39f9b3f823c5c7c5c5490" + integrity sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw== + dependencies: + regenerator-transform "^0.14.2" + "@babel/plugin-transform-regenerator@^7.4.5": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f" @@ -839,6 +1410,13 @@ dependencies: regenerator-transform "^0.14.0" +"@babel/plugin-transform-reserved-words@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz#0fc1027312b4d1c3276a57890c8ae3bcc0b64a86" + integrity sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-reserved-words@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz#4792af87c998a49367597d07fedf02636d2e1634" @@ -856,6 +1434,23 @@ resolve "^1.8.1" semver "^5.5.1" +"@babel/plugin-transform-runtime@^7.9.0": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.10.1.tgz#fd1887f749637fb2ed86dc278e79eb41df37f4b1" + integrity sha512-4w2tcglDVEwXJ5qxsY++DgWQdNJcCCsPxfT34wCUwIf2E7dI7pMpH8JczkMBbgBTNzBX62SZlNJ9H+De6Zebaw== + dependencies: + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz#e8b54f238a1ccbae482c4dce946180ae7b3143f3" + integrity sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-shorthand-properties@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0" @@ -863,6 +1458,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-spread@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz#0c6d618a0c4461a274418460a28c9ccf5239a7c8" + integrity sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-spread@^7.2.0": version "7.2.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406" @@ -877,6 +1479,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-sticky-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz#90fc89b7526228bed9842cff3588270a7a393b00" + integrity sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-regex" "^7.10.1" + "@babel/plugin-transform-sticky-regex@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1" @@ -885,6 +1495,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.0.0" +"@babel/plugin-transform-template-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz#914c7b7f4752c570ea00553b4284dad8070e8628" + integrity sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-template-literals@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0" @@ -893,6 +1511,13 @@ "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-typeof-symbol@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz#60c0239b69965d166b80a84de7315c1bc7e0bb0e" + integrity sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-typeof-symbol@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2" @@ -900,6 +1525,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-typescript@^7.9.0": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.10.1.tgz#2c54daea231f602468686d9faa76f182a94507a6" + integrity sha512-v+QWKlmCnsaimLeqq9vyCsVRMViZG1k2SZTlcZvB+TqyH570Zsij8nvVUZzOASCRiQFUxkLrn9Wg/kH0zgy5OQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-typescript" "^7.10.1" + "@babel/plugin-transform-typescript@~7.4.0": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.4.5.tgz#ab3351ba35307b79981993536c93ff8be050ba28" @@ -908,6 +1542,21 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-typescript" "^7.2.0" +"@babel/plugin-transform-unicode-escapes@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.1.tgz#add0f8483dab60570d9e03cecef6c023aa8c9940" + integrity sha512-zZ0Poh/yy1d4jeDWpx/mNwbKJVwUYJX73q+gyh4bwtG0/iUlzdEu0sLMda8yuDFS6LBQlT/ST1SJAR6zYwXWgw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-transform-unicode-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz#6b58f2aea7b68df37ac5025d9c88752443a6b43f" + integrity sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-unicode-regex@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f" @@ -934,6 +1583,14 @@ core-js "^2.6.5" regenerator-runtime "^0.13.2" +"@babel/polyfill@^7.8.3", "@babel/polyfill@^7.8.7": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.10.1.tgz#d56d4c8be8dd6ec4dce2649474e9b707089f739f" + integrity sha512-TviueJ4PBW5p48ra8IMtLXVkDucrlOZAIZ+EXqS3Ot4eukHbWiqcn7DcqpA1k5PcKtmJ4Xl9xwdv6yQvvcA+3g== + dependencies: + core-js "^2.6.5" + regenerator-runtime "^0.13.4" + "@babel/preset-env@^7.0.0": version "7.5.4" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.4.tgz#64bc15041a3cbb0798930319917e70fcca57713d" @@ -1046,6 +1703,87 @@ js-levenshtein "^1.1.3" semver "^5.5.0" +"@babel/preset-env@^7.9.0": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.10.2.tgz#715930f2cf8573b0928005ee562bed52fb65fdfb" + integrity sha512-MjqhX0RZaEgK/KueRzh+3yPSk30oqDKJ5HP5tqTSB1e2gzGS3PLy7K0BIpnp78+0anFuSwOeuCf1zZO7RzRvEA== + dependencies: + "@babel/compat-data" "^7.10.1" + "@babel/helper-compilation-targets" "^7.10.2" + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-proposal-async-generator-functions" "^7.10.1" + "@babel/plugin-proposal-class-properties" "^7.10.1" + "@babel/plugin-proposal-dynamic-import" "^7.10.1" + "@babel/plugin-proposal-json-strings" "^7.10.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.1" + "@babel/plugin-proposal-numeric-separator" "^7.10.1" + "@babel/plugin-proposal-object-rest-spread" "^7.10.1" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.1" + "@babel/plugin-proposal-optional-chaining" "^7.10.1" + "@babel/plugin-proposal-private-methods" "^7.10.1" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.1" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.1" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.10.1" + "@babel/plugin-transform-arrow-functions" "^7.10.1" + "@babel/plugin-transform-async-to-generator" "^7.10.1" + "@babel/plugin-transform-block-scoped-functions" "^7.10.1" + "@babel/plugin-transform-block-scoping" "^7.10.1" + "@babel/plugin-transform-classes" "^7.10.1" + "@babel/plugin-transform-computed-properties" "^7.10.1" + "@babel/plugin-transform-destructuring" "^7.10.1" + "@babel/plugin-transform-dotall-regex" "^7.10.1" + "@babel/plugin-transform-duplicate-keys" "^7.10.1" + "@babel/plugin-transform-exponentiation-operator" "^7.10.1" + "@babel/plugin-transform-for-of" "^7.10.1" + "@babel/plugin-transform-function-name" "^7.10.1" + "@babel/plugin-transform-literals" "^7.10.1" + "@babel/plugin-transform-member-expression-literals" "^7.10.1" + "@babel/plugin-transform-modules-amd" "^7.10.1" + "@babel/plugin-transform-modules-commonjs" "^7.10.1" + "@babel/plugin-transform-modules-systemjs" "^7.10.1" + "@babel/plugin-transform-modules-umd" "^7.10.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" + "@babel/plugin-transform-new-target" "^7.10.1" + "@babel/plugin-transform-object-super" "^7.10.1" + "@babel/plugin-transform-parameters" "^7.10.1" + "@babel/plugin-transform-property-literals" "^7.10.1" + "@babel/plugin-transform-regenerator" "^7.10.1" + "@babel/plugin-transform-reserved-words" "^7.10.1" + "@babel/plugin-transform-shorthand-properties" "^7.10.1" + "@babel/plugin-transform-spread" "^7.10.1" + "@babel/plugin-transform-sticky-regex" "^7.10.1" + "@babel/plugin-transform-template-literals" "^7.10.1" + "@babel/plugin-transform-typeof-symbol" "^7.10.1" + "@babel/plugin-transform-unicode-escapes" "^7.10.1" + "@babel/plugin-transform-unicode-regex" "^7.10.1" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.10.2" + browserslist "^4.12.0" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/preset-modules@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72" + integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + "@babel/runtime@7.3.4": version "7.3.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83" @@ -1074,6 +1812,13 @@ dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.8.4", "@babel/runtime@^7.9.0": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839" + integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.8.7": version "7.9.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" @@ -1090,6 +1835,15 @@ "@babel/parser" "^7.4.4" "@babel/types" "^7.4.4" +"@babel/template@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" + integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== + dependencies: + "@babel/code-frame" "^7.10.1" + "@babel/parser" "^7.10.1" + "@babel/types" "^7.10.1" + "@babel/template@^7.4.0", "@babel/template@^7.6.0": version "7.6.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6" @@ -1099,15 +1853,6 @@ "@babel/parser" "^7.6.0" "@babel/types" "^7.6.0" -"@babel/template@^7.8.3": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" - "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6": version "7.4.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.0.tgz#14006967dd1d2b3494cdd650c686db9daf0ddada" @@ -1123,6 +1868,21 @@ globals "^11.1.0" lodash "^4.17.11" +"@babel/traverse@^7.10.1", "@babel/traverse@^7.7.0": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27" + integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ== + dependencies: + "@babel/code-frame" "^7.10.1" + "@babel/generator" "^7.10.1" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/parser" "^7.10.1" + "@babel/types" "^7.10.1" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + "@babel/traverse@^7.2.4", "@babel/traverse@^7.3.4", "@babel/traverse@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb" @@ -1153,21 +1913,6 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/traverse@^7.7.0": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442" - integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.6" - "@babel/types" "^7.9.6" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - "@babel/types@^7.0.0", "@babel/types@^7.1.6", "@babel/types@^7.2.0": version "7.4.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.0.tgz#670724f77d24cce6cc7d8cf64599d511d164894c" @@ -1177,6 +1922,15 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" +"@babel/types@^7.10.1", "@babel/types@^7.10.2", "@babel/types@^7.7.0": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" + integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng== + dependencies: + "@babel/helper-validator-identifier" "^7.10.1" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + "@babel/types@^7.3.2", "@babel/types@^7.3.4", "@babel/types@^7.4.4", "@babel/types@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a" @@ -1204,15 +1958,6 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" -"@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.5", "@babel/types@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" - integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== - dependencies: - "@babel/helper-validator-identifier" "^7.9.5" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - "@cnakazawa/watch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" @@ -1308,7 +2053,7 @@ ember-cli-typescript "^2.0.2" heimdalljs "^0.3.0" -"@ember-decorators/component@^6.1.0": +"@ember-decorators/component@^6.1.0", "@ember-decorators/component@^6.1.1": version "6.1.1" resolved "https://registry.yarnpkg.com/@ember-decorators/component/-/component-6.1.1.tgz#b360dc4fa8e576ee1c840879399ef1745fd96e06" integrity sha512-Cj8tY/c0MC/rsipqsiWLh3YVN72DK92edPYamD/HzvftwzC6oDwawWk8RmStiBnG9PG/vntAt41l3S7HSSA+1Q== @@ -1316,6 +2061,14 @@ "@ember-decorators/utils" "^6.1.1" ember-cli-babel "^7.1.3" +"@ember-decorators/object@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@ember-decorators/object/-/object-6.1.1.tgz#50c922f5ac9af3ddd381cb6a43a031dfd9a70c7a" + integrity sha512-cb4CNR9sRoA31J3FCOFLDuR9ztM4wO9w1WlS4JeNRS7Z69SlB/XSXB/vplA3i9OOaXEy/zKWbu5ndZrHz0gvLw== + dependencies: + "@ember-decorators/utils" "^6.1.1" + ember-cli-babel "^7.1.3" + "@ember-decorators/utils@^6.1.1": version "6.1.1" resolved "https://registry.yarnpkg.com/@ember-decorators/utils/-/utils-6.1.1.tgz#6b619814942b4fb3747cfa9f540c9f05283d7c5e" @@ -2080,6 +2833,21 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== +"@types/fs-extra@^5.0.5": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.1.0.tgz#2a325ef97901504a3828718c390d34b8426a10a1" + integrity sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ== + dependencies: + "@types/node" "*" + +"@types/glob@*": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.2.tgz#06ca26521353a545d94a0adc74f38a59d232c987" + integrity sha512-VgNIkxK+j7Nz5P7jvUZlRvhuPSmsEfS03b0alKcq5V/STUKAa3Plemsn5mrQUO7am6OErJ4rhGEGJbACclrtRA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + "@types/glob@^7.1.1": version "7.1.1" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" @@ -2166,6 +2934,14 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/rimraf@^2.0.2": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.4.tgz#403887b0b53c6100a6c35d2ab24f6ccc042fec46" + integrity sha512-8gBudvllD2A/c0CcEX/BivIDorHFt5UI5m46TsNj8DjWCCTTZT74kEe4g+QsY7P/B9WdO98d82zZgXO/RQzu2Q== + dependencies: + "@types/glob" "*" + "@types/node" "*" + "@types/symlink-or-copy@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/symlink-or-copy/-/symlink-or-copy-1.2.0.tgz#4151a81b4052c80bc2becbae09f3a9ec010a9c7a" @@ -3288,6 +4064,20 @@ babel-plugin-dynamic-import-node@^2.3.0: dependencies: object.assign "^4.1.0" +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-ember-data-packages-polyfill@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/babel-plugin-ember-data-packages-polyfill/-/babel-plugin-ember-data-packages-polyfill-0.1.2.tgz#21154c095ddc703722b1fb8bb06c126c0b6d77dc" + integrity sha512-kTHnOwoOXfPXi00Z8yAgyD64+jdSXk3pknnS7NlqnCKAU6YDkXZ4Y7irl66kaZjZn0FBBt0P4YOZFZk85jYOww== + dependencies: + "@ember-data/rfc395-data" "^0.0.4" + babel-plugin-ember-modules-api-polyfill@^2.12.0, babel-plugin-ember-modules-api-polyfill@^2.6.0: version "2.12.0" resolved "https://registry.yarnpkg.com/babel-plugin-ember-modules-api-polyfill/-/babel-plugin-ember-modules-api-polyfill-2.12.0.tgz#a5e703205ba4e625a7fab9bb1aea64ef3222cf75" @@ -3295,6 +4085,13 @@ babel-plugin-ember-modules-api-polyfill@^2.12.0, babel-plugin-ember-modules-api- dependencies: ember-rfc176-data "^0.3.12" +babel-plugin-ember-modules-api-polyfill@^2.13.4: + version "2.13.4" + resolved "https://registry.yarnpkg.com/babel-plugin-ember-modules-api-polyfill/-/babel-plugin-ember-modules-api-polyfill-2.13.4.tgz#cf62bc9bfd808c48d810d5194f4329e9453bd603" + integrity sha512-uxQPkEQAzCYdwhZk16O9m1R4xtCRNy4oEUTBrccOPfzlIahRZJic/JeP/ZEL0BC6Mfq6r55eOg6gMF/zdFoCvA== + dependencies: + ember-rfc176-data "^0.3.13" + babel-plugin-ember-modules-api-polyfill@^2.8.0, babel-plugin-ember-modules-api-polyfill@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/babel-plugin-ember-modules-api-polyfill/-/babel-plugin-ember-modules-api-polyfill-2.9.0.tgz#8503e7b4192aeb336b00265e6235258ff6b754aa" @@ -4188,6 +4985,23 @@ broccoli-babel-transpiler@^7.2.0, broccoli-babel-transpiler@^7.3.0: rsvp "^4.8.4" workerpool "^3.1.1" +broccoli-babel-transpiler@^7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-7.4.0.tgz#f3069f0f77e8017aa17e1e757dfb4a30de044182" + integrity sha512-DzPXQr1C+zOgzXG40wqPjtjSSa6wRKb+Ls45Qtq7Pn+GxL3/jIvQOBZi0/irZ5dlYVbRMEZiUnaIBIOha2ygIw== + dependencies: + "@babel/core" "^7.8.3" + "@babel/polyfill" "^7.8.3" + broccoli-funnel "^2.0.2" + broccoli-merge-trees "^3.0.2" + broccoli-persistent-filter "^2.2.1" + clone "^2.1.2" + hash-for-dep "^1.4.7" + heimdalljs-logger "^0.1.9" + json-stable-stringify "^1.0.1" + rsvp "^4.8.4" + workerpool "^3.1.1" + broccoli-builder@^0.18.14: version "0.18.14" resolved "https://registry.yarnpkg.com/broccoli-builder/-/broccoli-builder-0.18.14.tgz#4b79e2f844de11a4e1b816c3f49c6df4776c312d" @@ -4852,6 +5666,16 @@ browserslist@^4.0.0: node-releases "^1.1.53" pkg-up "^2.0.0" +browserslist@^4.12.0, browserslist@^4.8.5: + version "4.12.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" + integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== + dependencies: + caniuse-lite "^1.0.30001043" + electron-to-chromium "^1.3.413" + node-releases "^1.1.53" + pkg-up "^2.0.0" + browserslist@^4.6.0: version "4.6.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.4.tgz#fd0638b3f8867fec2c604ed0ed9300379f8ec7c2" @@ -5097,6 +5921,11 @@ caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30000998: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000999.tgz#427253a69ad7bea4aa8d8345687b8eec51ca0e43" integrity sha512-1CUyKyecPeksKwXZvYw0tEoaMCo/RwBlXmEtN5vVnabvO0KPd9RQLcaAuR9/1F+KDMv6esmOFWlsXuzDk+8rxg== +caniuse-lite@^1.0.30001043: + version "1.0.30001081" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001081.tgz#40615a3c416a047c5a4d45673e5257bf128eb3b5" + integrity sha512-iZdh3lu09jsUtLE6Bp8NAbJskco4Y3UDtkR3GTCJGsbMowBU5IWDFF79sV2ws7lSqTzWyKazxam2thasHymENQ== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -5665,7 +6494,7 @@ convert-source-map@^1.1.0, convert-source-map@^1.5.0: dependencies: safe-buffer "~5.1.1" -convert-source-map@^1.5.1: +convert-source-map@^1.5.1, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -5724,6 +6553,14 @@ core-js-compat@^3.1.1: browserslist "^4.6.6" semver "^6.3.0" +core-js-compat@^3.6.2: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" + integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== + dependencies: + browserslist "^4.8.5" + semver "7.0.0" + core-js-pure@^3.0.1: version "3.2.1" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.2.1.tgz#879a23699cff46175bfd2d09158b5c50645a3c45" @@ -6521,6 +7358,11 @@ electron-to-chromium@^1.3.247: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.277.tgz#38b7b297f9b3f67ea900a965c1b11a555de526ec" integrity sha512-Czmsrgng89DOgJlIknnw9bn5431QdtnUwGp5YYiPwU1DbZQUxCLF+rc1ZC09VNAdalOPcvH6AE8BaA0H5HjI/w== +electron-to-chromium@^1.3.413: + version "1.3.466" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.466.tgz#89f716db3afc4bb482ea2aaaa16c4808f89f762a" + integrity sha512-eieqkoM2hCkZZRhETKyCouMziDV3l4XEKHRLuzcHG+HV+P7PeODU/z9HAmBgMQkzvHg2DoyQhfIDmmeguLZT/Q== + elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" @@ -6617,6 +7459,14 @@ ember-can@^2.0.0: ember-cli-babel "7.8.0" ember-inflector "3.0.1" +ember-classic-decorator@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/ember-classic-decorator/-/ember-classic-decorator-1.0.8.tgz#e290e5b0b1a31a569587a85a9c5c7a2f1242cabb" + integrity sha512-IsCDJ7rLsrFjYtgi9UXUmjzUQJaaJzmy/gKwGGtZ6kZwT5yhzSbScRi0P6Cb0guJPtlMMCE0sAQpJRbXmBb/gA== + dependencies: + babel-plugin-filter-imports "^3.0.0" + ember-cli-babel "^7.11.1" + ember-cli-addon-docs-yuidoc@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/ember-cli-addon-docs-yuidoc/-/ember-cli-addon-docs-yuidoc-0.2.3.tgz#13f7943c7ef1909d74ef9d33a719236117c17262" @@ -6734,6 +7584,38 @@ ember-cli-babel@^7.1.3, ember-cli-babel@^7.10.0, ember-cli-babel@^7.11.0, ember- ensure-posix-path "^1.0.2" semver "^5.5.0" +ember-cli-babel@^7.11.1: + version "7.20.5" + resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.20.5.tgz#ab60dcf7371f4d86d41c2596c6d050470e283074" + integrity sha512-lWvKqJPQ1KJigWKmxREqFbXgZd2/nFKMg3chiiNejpsAIgGLPXGFLwpMQ7QYCRrlims7D/bnDgFvXuOLkw0fcQ== + dependencies: + "@babel/core" "^7.9.0" + "@babel/helper-compilation-targets" "^7.8.7" + "@babel/plugin-proposal-class-properties" "^7.8.3" + "@babel/plugin-proposal-decorators" "^7.8.3" + "@babel/plugin-transform-modules-amd" "^7.9.0" + "@babel/plugin-transform-runtime" "^7.9.0" + "@babel/plugin-transform-typescript" "^7.9.0" + "@babel/polyfill" "^7.8.7" + "@babel/preset-env" "^7.9.0" + "@babel/runtime" "^7.9.0" + amd-name-resolver "^1.2.1" + babel-plugin-debug-macros "^0.3.0" + babel-plugin-ember-data-packages-polyfill "^0.1.2" + babel-plugin-ember-modules-api-polyfill "^2.13.4" + babel-plugin-module-resolver "^3.1.1" + broccoli-babel-transpiler "^7.4.0" + broccoli-debug "^0.6.4" + broccoli-funnel "^2.0.1" + broccoli-source "^1.1.0" + clone "^2.1.2" + ember-cli-babel-plugin-helpers "^1.1.0" + ember-cli-version-checker "^4.1.0" + ensure-posix-path "^1.0.2" + fixturify-project "^1.10.0" + rimraf "^3.0.1" + semver "^5.5.0" + ember-cli-broccoli-sane-watcher@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ember-cli-broccoli-sane-watcher/-/ember-cli-broccoli-sane-watcher-3.0.0.tgz#dc1812c047e1ceec4413d3c41b51a9ffc61b4cfe" @@ -7045,6 +7927,15 @@ ember-cli-version-checker@^3.0.0, ember-cli-version-checker@^3.1.2, ember-cli-ve resolve-package-path "^1.2.6" semver "^5.6.0" +ember-cli-version-checker@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ember-cli-version-checker/-/ember-cli-version-checker-4.1.1.tgz#27b938228306cb0dbc4f74e95c536cdd6448e499" + integrity sha512-bzEWsTMXUGEJfxcAGWPe6kI7oHEGD3jaxUWDYPTqzqGhNkgPwXTBgoWs9zG1RaSMaOPFnloWuxRcoHi4TrYS3Q== + dependencies: + resolve-package-path "^2.0.0" + semver "^6.3.0" + silent-error "^1.1.1" + ember-cli@~3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-3.12.0.tgz#e6d27e4c57fc44eb7963fe4611b3b0cfb9d9558d" @@ -7213,6 +8104,15 @@ ember-debug-handlers-polyfill@^1.1.1: resolved "https://registry.yarnpkg.com/ember-debug-handlers-polyfill/-/ember-debug-handlers-polyfill-1.1.1.tgz#e9ae0a720271a834221179202367421b580002ef" integrity sha512-lO7FBAqJjzbL+IjnWhVfQITypPOJmXdZngZR/Vdn513W4g/Q6Sjicao/mDzeDCb48Y70C4Facwk0LjdIpSZkRg== +ember-decorators@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/ember-decorators/-/ember-decorators-6.1.1.tgz#6d770f8999cf5a413a1ee459afd520838c0fc470" + integrity sha512-63vZPntPn1aqMyeNRLoYjJD+8A8obd+c2iZkJflswpDRNVIsp2m7aQdSCtPt4G0U/TEq2251g+N10maHX3rnJQ== + dependencies: + "@ember-decorators/component" "^6.1.1" + "@ember-decorators/object" "^6.1.1" + ember-cli-babel "^7.7.3" + ember-element-helper@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ember-element-helper/-/ember-element-helper-0.1.1.tgz#ffd2a0566b22a13c0e7780ae96443f4ffda2e63d" @@ -7432,7 +8332,7 @@ ember-responsive@^3.0.4: dependencies: ember-cli-babel "^6.6.0" -ember-rfc176-data@^0.3.11: +ember-rfc176-data@^0.3.11, ember-rfc176-data@^0.3.13: version "0.3.13" resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.13.tgz#ed1712a26e65fec703655f35410414aa1982cf3b" integrity sha512-m9JbwQlT6PjY7x/T8HslnXP7Sz9bx/pz3FrNfNi2NesJnbNISly0Lix6NV1fhfo46572cpq4jrM+/6yYlMefTQ== @@ -8444,6 +9344,25 @@ fireworm@^0.7.0: lodash.flatten "^3.0.2" minimatch "^3.0.2" +fixturify-project@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/fixturify-project/-/fixturify-project-1.10.0.tgz#091c452a9bb15f09b6b9cc7cf5c0ad559f1d9aad" + integrity sha512-L1k9uiBQuN0Yr8tA9Noy2VSQ0dfg0B8qMdvT7Wb5WQKc7f3dn3bzCbSrqlb+etLW+KDV4cBC7R1OvcMg3kcxmA== + dependencies: + fixturify "^1.2.0" + tmp "^0.0.33" + +fixturify@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fixturify/-/fixturify-1.3.0.tgz#163c468093c7c4d90b70cde39fd6325f6528b25d" + integrity sha512-tL0svlOy56pIMMUQ4bU1xRe6NZbFSa/ABTWMxW2mH38lFGc9TrNAKWcMBQ7eIjo3wqSS8f2ICabFaatFyFmrVQ== + dependencies: + "@types/fs-extra" "^5.0.5" + "@types/minimatch" "^3.0.3" + "@types/rimraf" "^2.0.2" + fs-extra "^7.0.1" + matcher-collection "^2.0.0" + flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -8744,6 +9663,11 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -10300,6 +11224,13 @@ json5@^2.1.0: dependencies: minimist "^1.2.0" +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -10392,6 +11323,18 @@ leek@0.0.24: lodash.assign "^3.2.0" rsvp "^3.0.21" +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levenary@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" + integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== + dependencies: + leven "^3.1.0" + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -11033,6 +11976,14 @@ matcher-collection@^1.0.0, matcher-collection@^1.0.5, matcher-collection@^1.1.1: dependencies: minimatch "^3.0.2" +matcher-collection@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/matcher-collection/-/matcher-collection-2.0.1.tgz#90be1a4cf58d6f2949864f65bb3b0f3e41303b29" + integrity sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ== + dependencies: + "@types/minimatch" "^3.0.3" + minimatch "^3.0.2" + material-colors@^1.2.1: version "1.2.6" resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" @@ -13129,6 +14080,13 @@ regenerate-unicode-properties@^8.0.2, regenerate-unicode-properties@^8.1.0: dependencies: regenerate "^1.4.0" +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== + dependencies: + regenerate "^1.4.0" + regenerate@^1.2.1, regenerate@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" @@ -13180,6 +14138,14 @@ regenerator-transform@^0.14.0: dependencies: private "^0.1.6" +regenerator-transform@^0.14.2: + version "0.14.4" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7" + integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw== + dependencies: + "@babel/runtime" "^7.8.4" + private "^0.1.8" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -13238,6 +14204,18 @@ regexpu-core@^4.6.0: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.1.0" +regexpu-core@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" + integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + regjsgen@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" @@ -13248,6 +14226,11 @@ regjsgen@^0.5.0: resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== +regjsgen@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== + regjsparser@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" @@ -13262,6 +14245,13 @@ regjsparser@^0.6.0: dependencies: jsesc "~0.5.0" +regjsparser@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" + integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== + dependencies: + jsesc "~0.5.0" + relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" @@ -13413,6 +14403,14 @@ resolve-package-path@^1.0.11, resolve-package-path@^1.2.2, resolve-package-path@ path-root "^0.1.1" resolve "^1.10.0" +resolve-package-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-package-path/-/resolve-package-path-2.0.0.tgz#7f258ab86ff074fff4ff8027a28f94d17d6fb1df" + integrity sha512-/CLuzodHO2wyyHTzls5Qr+EFeG6RcW4u6//gjYvUfcfyuplIX1SSccU+A5A9A78Gmezkl3NBkFAMxLbzTY9TJA== + dependencies: + path-root "^0.1.1" + resolve "^1.13.1" + resolve-path@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7" @@ -13461,7 +14459,7 @@ resolve@^1.10.0, resolve@^1.3.3, resolve@^1.8.1: dependencies: path-parse "^1.0.6" -resolve@^1.12.0: +resolve@^1.12.0, resolve@^1.13.1: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -13502,6 +14500,13 @@ rimraf@^2.2.8, rimraf@^2.3.4, rimraf@^2.4.1, rimraf@^2.4.3, rimraf@^2.4.4, rimra dependencies: glob "^7.1.3" +rimraf@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -13715,6 +14720,11 @@ semver-regex@^2.0.0: resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + semver@^5.3.0, semver@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -15153,6 +16163,11 @@ unicode-match-property-value-ecmascript@^1.1.0: resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== + unicode-property-aliases-ecmascript@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" From ddfd9cc7506182bc1ba1f4b2727876069d3b4b12 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Wed, 10 Jun 2020 09:07:16 -0500 Subject: [PATCH 02/14] Add fixes for ESLint getter-return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …I GUESS --- ui/app/adapters/application.js | 2 +- ui/app/components/allocation-row.js | 2 +- ui/app/components/allocation-stat.js | 6 +++--- ui/app/components/fs/browser.js | 2 +- ui/app/components/fs/file.js | 2 +- ui/app/components/image-file.js | 2 +- ui/app/components/lifecycle-chart-row.js | 4 ++-- ui/app/components/task-row.js | 4 ++-- ui/app/models/job.js | 4 ++-- ui/app/models/node-attributes.js | 2 +- ui/app/models/node.js | 2 +- ui/app/services/token.js | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ui/app/adapters/application.js b/ui/app/adapters/application.js index 3373fef90..5a4d99804 100644 --- a/ui/app/adapters/application.js +++ b/ui/app/adapters/application.js @@ -28,7 +28,7 @@ export default class Application extends RESTAdapter { }; } - return; + return undefined; } handleResponse(status, headers, payload) { diff --git a/ui/app/components/allocation-row.js b/ui/app/components/allocation-row.js index c5d829b26..0a9fb1c0a 100644 --- a/ui/app/components/allocation-row.js +++ b/ui/app/components/allocation-row.js @@ -30,7 +30,7 @@ export default class AllocationRow extends Component { @computed('allocation', 'allocation.isRunning') get stats() { - if (!this.get('allocation.isRunning')) return; + if (!this.get('allocation.isRunning')) return undefined; return AllocationStatsTracker.create({ fetch: url => this.token.authorizedRequest(url), diff --git a/ui/app/components/allocation-stat.js b/ui/app/components/allocation-stat.js index b740910e0..814d43e52 100644 --- a/ui/app/components/allocation-stat.js +++ b/ui/app/components/allocation-stat.js @@ -29,12 +29,12 @@ export default class AllocationStat extends Component { return this[this.metric]; } - return; + return undefined; } @computed('metric', 'stat.used') get formattedStat() { - if (!this.stat) return; + if (!this.stat) return undefined; if (this.metric === 'memory') return formatBytes([this.stat.used]); return this.stat.used; } @@ -43,6 +43,6 @@ export default class AllocationStat extends Component { get formattedReserved() { if (this.metric === 'memory') return `${this.statsTracker.reservedMemory} MiB`; if (this.metric === 'cpu') return `${this.statsTracker.reservedCPU} MHz`; - return; + return undefined; } } diff --git a/ui/app/components/fs/browser.js b/ui/app/components/fs/browser.js index d18a15777..7dbfd7183 100644 --- a/ui/app/components/fs/browser.js +++ b/ui/app/components/fs/browser.js @@ -24,7 +24,7 @@ export default class Browser extends Component { return this.model; } - return; + return undefined; } @computed('taskState') diff --git a/ui/app/components/fs/file.js b/ui/app/components/fs/file.js index 4b282bb1c..e977c7519 100644 --- a/ui/app/components/fs/file.js +++ b/ui/app/components/fs/file.js @@ -69,7 +69,7 @@ export default class File extends Component { return 'readat'; } - return; + return undefined; } @computed('allocation.{id,node.httpAddr}', 'fetchMode', 'useServer') diff --git a/ui/app/components/image-file.js b/ui/app/components/image-file.js index 2e359a171..16ee6733c 100644 --- a/ui/app/components/image-file.js +++ b/ui/app/components/image-file.js @@ -19,7 +19,7 @@ export default class ImageFile extends Component { @computed('src') get fileName() { - if (!this.src) return; + if (!this.src) return undefined; return this.src.includes('/') ? this.src.match(/^.*\/(.*)$/)[1] : this.src; } diff --git a/ui/app/components/lifecycle-chart-row.js b/ui/app/components/lifecycle-chart-row.js index f63a50eba..44985d747 100644 --- a/ui/app/components/lifecycle-chart-row.js +++ b/ui/app/components/lifecycle-chart-row.js @@ -12,7 +12,7 @@ export default class LifecycleChartRow extends Component { return 'is-active'; } - return; + return undefined; } @computed('taskState.finishedAt') @@ -21,6 +21,6 @@ export default class LifecycleChartRow extends Component { return 'is-finished'; } - return; + return undefined; } } diff --git a/ui/app/components/task-row.js b/ui/app/components/task-row.js index feeb36ef1..28094acd1 100644 --- a/ui/app/components/task-row.js +++ b/ui/app/components/task-row.js @@ -29,14 +29,14 @@ export default class TaskRow extends Component { // Since all tasks for an allocation share the same tracker, use the registry @computed('task', 'task.isRunning') get stats() { - if (!this.get('task.isRunning')) return; + if (!this.get('task.isRunning')) return undefined; return this.statsTrackersRegistry.getTracker(this.get('task.allocation')); } @computed('task.name', 'stats.tasks.[]') get taskStats() { - if (!this.stats) return; + if (!this.stats) return undefined; return this.get('stats.tasks').findBy('task', this.get('task.name')); } diff --git a/ui/app/models/job.js b/ui/app/models/job.js index 9d5b495e6..e179cd4b4 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -174,7 +174,7 @@ export default class Job extends Model { return failureEvaluations.sortBy('modifyIndex').get('lastObject'); } - return; + return undefined; } @equal('type', 'service') supportsDeployments; @@ -185,7 +185,7 @@ export default class Job extends Model { get runningDeployment() { const latest = this.latestDeployment; if (latest.get('isRunning')) return latest; - return; + return undefined; } fetchRawDefinition() { diff --git a/ui/app/models/node-attributes.js b/ui/app/models/node-attributes.js index 2f24d9bb9..810fab2c6 100644 --- a/ui/app/models/node-attributes.js +++ b/ui/app/models/node-attributes.js @@ -13,7 +13,7 @@ export default class NodeAttributes extends Fragment { const original = this.nodeAttributes; if (!original) { - return; + return undefined; } // `unflatten` doesn't sort keys before unflattening, so manual preprocessing is necessary. diff --git a/ui/app/models/node.js b/ui/app/models/node.js index d56d28ca6..2e69d56aa 100644 --- a/ui/app/models/node.js +++ b/ui/app/models/node.js @@ -74,7 +74,7 @@ export default class Node extends Model { return allocation.modifyTime; } - return; + return undefined; } @fragmentArray('node-driver') drivers; diff --git a/ui/app/services/token.js b/ui/app/services/token.js index d829431d9..78d43feb1 100644 --- a/ui/app/services/token.js +++ b/ui/app/services/token.js @@ -47,7 +47,7 @@ export default class TokenService extends Service { @computed('secret', 'fetchSelfToken.lastSuccessful.value') get selfToken() { if (this.secret) return this.get('fetchSelfToken.lastSuccessful.value'); - return; + return undefined; } @task(function*() { From 26b7b3fa06d541a196dacc101374560cab049a92 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Wed, 10 Jun 2020 10:24:10 -0500 Subject: [PATCH 03/14] Remove unnecessary fetch-setting Originally this was failing because it only had a getter. I tried replacing it with a computed property and that succeeded, but since we have already stopped using jQuery, we might as well remove it. --- ui/app/adapters/application.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui/app/adapters/application.js b/ui/app/adapters/application.js index 5a4d99804..60a6616c8 100644 --- a/ui/app/adapters/application.js +++ b/ui/app/adapters/application.js @@ -10,10 +10,6 @@ export const namespace = 'v1'; @classic export default class Application extends RESTAdapter { - // TODO: This can be removed once jquery-integration is turned off for - // the entire app. - useFetch = true; - namespace = namespace; @service system; From de9d39581dc51c5ec03f649ee908ad950b6733f6 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Thu, 11 Jun 2020 22:19:08 -0700 Subject: [PATCH 04/14] Change with-namespace-ids mixin to a base class This is a merge of 5d9fce5. --- ui/app/adapters/job.js | 5 +-- ui/app/adapters/volume.js | 5 +-- .../watchable-namespace-ids.js} | 37 +++++++++---------- 3 files changed, 22 insertions(+), 25 deletions(-) rename ui/app/{mixins/with-namespace-ids.js => adapters/watchable-namespace-ids.js} (70%) diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index 2bc323838..a0213bb64 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -1,8 +1,7 @@ -import Watchable from './watchable'; +import WatchableNamespaceIDs from './watchable-namespace-ids'; import addToPath from 'nomad-ui/utils/add-to-path'; -import WithNamespaceIDs from 'nomad-ui/mixins/with-namespace-ids'; -export default Watchable.extend(WithNamespaceIDs, { +export default WatchableNamespaceIDs.extend({ relationshipFallbackLinks: Object.freeze({ summary: '/summary', }), diff --git a/ui/app/adapters/volume.js b/ui/app/adapters/volume.js index 8f2d8532a..b6076bf76 100644 --- a/ui/app/adapters/volume.js +++ b/ui/app/adapters/volume.js @@ -1,7 +1,6 @@ -import Watchable from './watchable'; -import WithNamespaceIDs from 'nomad-ui/mixins/with-namespace-ids'; +import WatchableNamespaceIDs from './watchable-namespace-ids'; -export default Watchable.extend(WithNamespaceIDs, { +export default WatchableNamespaceIDs.extend({ queryParamsToAttrs: Object.freeze({ type: 'type', plugin_id: 'plugin.id', diff --git a/ui/app/mixins/with-namespace-ids.js b/ui/app/adapters/watchable-namespace-ids.js similarity index 70% rename from ui/app/mixins/with-namespace-ids.js rename to ui/app/adapters/watchable-namespace-ids.js index bdbdb9969..0e9c35998 100644 --- a/ui/app/mixins/with-namespace-ids.js +++ b/ui/app/adapters/watchable-namespace-ids.js @@ -1,57 +1,56 @@ import { inject as service } from '@ember/service'; -import Mixin from '@ember/object/mixin'; +import Watchable from './watchable'; -// eslint-disable-next-line ember/no-new-mixins -export default Mixin.create({ - system: service(), +export default class WatchableNamespaceIDs extends Watchable { + @service system; findAll() { const namespace = this.get('system.activeNamespace'); - return this._super(...arguments).then(data => { + return super.findAll(...arguments).then(data => { data.forEach(record => { record.Namespace = namespace ? namespace.get('id') : 'default'; }); return data; }); - }, + } findRecord(store, type, id, snapshot) { const [, namespace] = JSON.parse(id); const namespaceQuery = namespace && namespace !== 'default' ? { namespace } : {}; - return this._super(store, type, id, snapshot, namespaceQuery); - }, + return super.findRecord(store, type, id, snapshot, namespaceQuery); + } urlForFindAll() { - const url = this._super(...arguments); + const url = super.urlForFindAll(...arguments); const namespace = this.get('system.activeNamespace.id'); return associateNamespace(url, namespace); - }, + } urlForQuery() { - const url = this._super(...arguments); + const url = super.urlForQuery(...arguments); const namespace = this.get('system.activeNamespace.id'); return associateNamespace(url, namespace); - }, + } urlForFindRecord(id, type, hash) { const [name, namespace] = JSON.parse(id); - let url = this._super(name, type, hash); + let url = super.urlForFindRecord(name, type, hash); return associateNamespace(url, namespace); - }, + } urlForUpdateRecord(id, type, hash) { const [name, namespace] = JSON.parse(id); - let url = this._super(name, type, hash); + let url = super.urlForUpdateRecord(name, type, hash); return associateNamespace(url, namespace); - }, + } xhrKey(url, method, options = {}) { - const plainKey = this._super(...arguments); + const plainKey = super.xhrKey(...arguments); const namespace = options.data && options.data.namespace; return associateNamespace(plainKey, namespace); - }, -}); + } +} function associateNamespace(url, namespace) { if (namespace && namespace !== 'default') { From e077dc5d12c71e22618f6d84877e2f4b6b230d23 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Thu, 11 Jun 2020 22:20:59 -0700 Subject: [PATCH 05/14] Change URL-generation for job-updating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The id-processing in the WatchableNamespaceIds adapter was happening twice; this removes urlForUpdate record so it only happens once. @DingoEatingFuzz figured it out! 🥳 --- ui/app/adapters/application.js | 54 +++++++++++----------- ui/app/adapters/job.js | 1 + ui/app/adapters/watchable-namespace-ids.js | 6 --- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/ui/app/adapters/application.js b/ui/app/adapters/application.js index 60a6616c8..fcc23ed8f 100644 --- a/ui/app/adapters/application.js +++ b/ui/app/adapters/application.js @@ -86,36 +86,36 @@ export default class Application extends RESTAdapter { // // This is the original implementation of _buildURL // without the pluralization of modelName - urlForFindRecord = urlForRecord; + urlForFindRecord(id, modelName) { + let path; + let url = []; + let host = get(this, 'host'); + let prefix = this.urlPrefix(); - urlForUpdateRecord = urlForRecord; -} - -function urlForRecord(id, modelName) { - let path; - let url = []; - let host = get(this, 'host'); - let prefix = this.urlPrefix(); - - if (modelName) { - path = modelName.camelize(); - if (path) { - url.push(path); + if (modelName) { + path = modelName.camelize(); + if (path) { + url.push(path); + } } + + if (id) { + url.push(encodeURIComponent(id)); + } + + if (prefix) { + url.unshift(prefix); + } + + url = url.join('/'); + if (!host && url && url.charAt(0) !== '/') { + url = '/' + url; + } + + return url; } - if (id) { - url.push(encodeURIComponent(id)); + urlForUpdateRecord() { + return this.urlForFindRecord(...arguments); } - - if (prefix) { - url.unshift(prefix); - } - - url = url.join('/'); - if (!host && url && url.charAt(0) !== '/') { - url = '/' + url; - } - - return url; } diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index a0213bb64..10b0e1cb2 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -62,6 +62,7 @@ export default WatchableNamespaceIDs.extend({ update(job) { const jobId = job.get('id') || job.get('_idBeforeSaving'); + // debugger; return this.ajax(this.urlForUpdateRecord(jobId, 'job'), 'POST', { data: { Job: job.get('_newDefinitionJSON'), diff --git a/ui/app/adapters/watchable-namespace-ids.js b/ui/app/adapters/watchable-namespace-ids.js index 0e9c35998..24eeffe15 100644 --- a/ui/app/adapters/watchable-namespace-ids.js +++ b/ui/app/adapters/watchable-namespace-ids.js @@ -39,12 +39,6 @@ export default class WatchableNamespaceIDs extends Watchable { return associateNamespace(url, namespace); } - urlForUpdateRecord(id, type, hash) { - const [name, namespace] = JSON.parse(id); - let url = super.urlForUpdateRecord(name, type, hash); - return associateNamespace(url, namespace); - } - xhrKey(url, method, options = {}) { const plainKey = super.xhrKey(...arguments); const namespace = options.data && options.data.namespace; From 5d325351a518bd6cfdb641c553666a0d8da6c71e Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Thu, 11 Jun 2020 08:38:33 -0500 Subject: [PATCH 06/14] Fix query parameters structures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I’d think the codemod would handle this if it’s a requirement but apparently not, is it a bug? --- .../controllers/allocations/allocation/fs.js | 12 ++++--- .../allocations/allocation/task/fs.js | 12 ++++--- ui/app/controllers/application.js | 8 +++-- ui/app/controllers/clients/index.js | 36 +++++++++++++------ ui/app/controllers/csi/plugins/index.js | 20 +++++++---- .../csi/plugins/plugin/allocations.js | 24 +++++++++---- ui/app/controllers/csi/volumes.js | 8 +++-- ui/app/controllers/csi/volumes/index.js | 20 +++++++---- ui/app/controllers/jobs.js | 8 +++-- ui/app/controllers/jobs/index.js | 36 +++++++++++++------ ui/app/controllers/jobs/job/allocations.js | 20 +++++++---- ui/app/controllers/jobs/job/evaluations.js | 12 ++++--- ui/app/controllers/jobs/job/index.js | 16 ++++++--- ui/app/controllers/jobs/job/task-group.js | 20 +++++++---- ui/app/controllers/servers.js | 16 ++++++--- 15 files changed, 186 insertions(+), 82 deletions(-) diff --git a/ui/app/controllers/allocations/allocation/fs.js b/ui/app/controllers/allocations/allocation/fs.js index cf59dc753..4aa267505 100644 --- a/ui/app/controllers/allocations/allocation/fs.js +++ b/ui/app/controllers/allocations/allocation/fs.js @@ -2,10 +2,14 @@ import Controller from '@ember/controller'; import { computed } from '@ember/object'; export default class FsController extends Controller { - queryParams = { - sortProperty: 'sort', - sortDescending: 'desc', - }; + queryParams = [ + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; sortProperty = 'Name'; sortDescending = false; diff --git a/ui/app/controllers/allocations/allocation/task/fs.js b/ui/app/controllers/allocations/allocation/task/fs.js index cf27a5112..ee708fe28 100644 --- a/ui/app/controllers/allocations/allocation/task/fs.js +++ b/ui/app/controllers/allocations/allocation/task/fs.js @@ -2,10 +2,14 @@ import Controller from '@ember/controller'; import { computed } from '@ember/object'; export default class FsController extends Controller { - queryParams = { - sortProperty: 'sort', - sortDescending: 'desc', - }; + queryParams = [ + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; sortProperty = 'Name'; sortDescending = false; diff --git a/ui/app/controllers/application.js b/ui/app/controllers/application.js index fcebfdcd1..a6b8756de 100644 --- a/ui/app/controllers/application.js +++ b/ui/app/controllers/application.js @@ -14,9 +14,11 @@ export default class ApplicationController extends Controller { @service config; @service system; - queryParams = { - region: 'region', - }; + queryParams = [ + { + region: 'region', + }, + ]; region = null; diff --git a/ui/app/controllers/clients/index.js b/ui/app/controllers/clients/index.js index 0b7f57d15..31398ab85 100644 --- a/ui/app/controllers/clients/index.js +++ b/ui/app/controllers/clients/index.js @@ -21,16 +21,32 @@ export default class IndexController extends Controller.extend( @alias('model.nodes') nodes; @alias('model.agents') agents; - queryParams = { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - qpClass: 'class', - qpState: 'state', - qpDatacenter: 'dc', - qpVolume: 'volume', - }; + queryParams = [ + { + currentPage: 'page', + }, + { + searchTerm: 'search', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + { + qpClass: 'class', + }, + { + qpState: 'state', + }, + { + qpDatacenter: 'dc', + }, + { + qpVolume: 'volume', + }, + ]; currentPage = 1; @readOnly('userSettings.pageSize') pageSize; diff --git a/ui/app/controllers/csi/plugins/index.js b/ui/app/controllers/csi/plugins/index.js index 2ca4889ea..bd9b3d2e5 100644 --- a/ui/app/controllers/csi/plugins/index.js +++ b/ui/app/controllers/csi/plugins/index.js @@ -22,12 +22,20 @@ export default class IndexController extends Controller.extend( @alias('pluginsController.isForbidden') isForbidden; - queryParams = { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - }; + queryParams = [ + { + currentPage: 'page', + }, + { + searchTerm: 'search', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; currentPage = 1; @readOnly('userSettings.pageSize') pageSize; diff --git a/ui/app/controllers/csi/plugins/plugin/allocations.js b/ui/app/controllers/csi/plugins/plugin/allocations.js index 35a250a34..53f7712a4 100644 --- a/ui/app/controllers/csi/plugins/plugin/allocations.js +++ b/ui/app/controllers/csi/plugins/plugin/allocations.js @@ -13,13 +13,23 @@ export default class AllocationsController extends Controller.extend( ) { @service userSettings; - queryParams = { - currentPage: 'page', - sortProperty: 'sort', - sortDescending: 'desc', - qpHealth: 'healthy', - qpType: 'type', - }; + queryParams = [ + { + currentPage: 'page', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + { + qpHealth: 'healthy', + }, + { + qpType: 'type', + }, + ]; currentPage = 1; @readOnly('userSettings.pageSize') pageSize; diff --git a/ui/app/controllers/csi/volumes.js b/ui/app/controllers/csi/volumes.js index 1a48a6208..94772e3bc 100644 --- a/ui/app/controllers/csi/volumes.js +++ b/ui/app/controllers/csi/volumes.js @@ -1,9 +1,11 @@ import Controller from '@ember/controller'; export default class VolumesController extends Controller { - queryParams = { - volumeNamespace: 'namespace', - }; + queryParams = [ + { + volumeNamespace: 'namespace', + }, + ]; isForbidden = false; diff --git a/ui/app/controllers/csi/volumes/index.js b/ui/app/controllers/csi/volumes/index.js index 8f7abb9be..26771aa85 100644 --- a/ui/app/controllers/csi/volumes/index.js +++ b/ui/app/controllers/csi/volumes/index.js @@ -25,12 +25,20 @@ export default class IndexController extends Controller.extend( @alias('volumesController.isForbidden') isForbidden; - queryParams = { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - }; + queryParams = [ + { + currentPage: 'page', + }, + { + searchTerm: 'search', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; currentPage = 1; @readOnly('userSettings.pageSize') pageSize; diff --git a/ui/app/controllers/jobs.js b/ui/app/controllers/jobs.js index a077a5705..53b270e7f 100644 --- a/ui/app/controllers/jobs.js +++ b/ui/app/controllers/jobs.js @@ -4,9 +4,11 @@ import Controller from '@ember/controller'; export default class JobsController extends Controller { @service system; - queryParams = { - jobNamespace: 'namespace', - }; + queryParams = [ + { + jobNamespace: 'namespace', + }, + ]; isForbidden = false; diff --git a/ui/app/controllers/jobs/index.js b/ui/app/controllers/jobs/index.js index 1b2f2713c..c6312472f 100644 --- a/ui/app/controllers/jobs/index.js +++ b/ui/app/controllers/jobs/index.js @@ -18,16 +18,32 @@ export default class IndexController extends Controller.extend(Sortable, Searcha @alias('jobsController.isForbidden') isForbidden; - queryParams = { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - qpType: 'type', - qpStatus: 'status', - qpDatacenter: 'dc', - qpPrefix: 'prefix', - }; + queryParams = [ + { + currentPage: 'page', + }, + { + searchTerm: 'search', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + { + qpType: 'type', + }, + { + qpStatus: 'status', + }, + { + qpDatacenter: 'dc', + }, + { + qpPrefix: 'prefix', + }, + ]; currentPage = 1; @readOnly('userSettings.pageSize') pageSize; diff --git a/ui/app/controllers/jobs/job/allocations.js b/ui/app/controllers/jobs/job/allocations.js index f4688b3af..a01c19951 100644 --- a/ui/app/controllers/jobs/job/allocations.js +++ b/ui/app/controllers/jobs/job/allocations.js @@ -12,12 +12,20 @@ export default class AllocationsController extends Controller.extend( Searchable, WithNamespaceResetting ) { - queryParams = { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - }; + queryParams = [ + { + currentPage: 'page', + }, + { + searchTerm: 'search', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; currentPage = 1; pageSize = 25; diff --git a/ui/app/controllers/jobs/job/evaluations.js b/ui/app/controllers/jobs/job/evaluations.js index b88ee42e0..831cb3f7c 100644 --- a/ui/app/controllers/jobs/job/evaluations.js +++ b/ui/app/controllers/jobs/job/evaluations.js @@ -9,10 +9,14 @@ export default class EvaluationsController extends Controller.extend( WithNamespaceResetting, Sortable ) { - queryParams = { - sortProperty: 'sort', - sortDescending: 'desc', - }; + queryParams = [ + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; sortProperty = 'modifyIndex'; sortDescending = true; diff --git a/ui/app/controllers/jobs/job/index.js b/ui/app/controllers/jobs/job/index.js index 89ef1850d..eae91858f 100644 --- a/ui/app/controllers/jobs/job/index.js +++ b/ui/app/controllers/jobs/job/index.js @@ -9,11 +9,17 @@ import classic from 'ember-classic-decorator'; export default class IndexController extends Controller.extend(WithNamespaceResetting) { @service system; - queryParams = { - currentPage: 'page', - sortProperty: 'sort', - sortDescending: 'desc', - }; + queryParams = [ + { + currentPage: 'page', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; currentPage = 1; diff --git a/ui/app/controllers/jobs/job/task-group.js b/ui/app/controllers/jobs/job/task-group.js index 365ec12ae..a20fdc52f 100644 --- a/ui/app/controllers/jobs/job/task-group.js +++ b/ui/app/controllers/jobs/job/task-group.js @@ -15,12 +15,20 @@ export default class TaskGroupController extends Controller.extend( ) { @service userSettings; - queryParams = { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - }; + queryParams = [ + { + currentPage: 'page', + }, + { + searchTerm: 'search', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; currentPage = 1; @readOnly('userSettings.pageSize') pageSize; diff --git a/ui/app/controllers/servers.js b/ui/app/controllers/servers.js index c3374ca5d..b82e2bf58 100644 --- a/ui/app/controllers/servers.js +++ b/ui/app/controllers/servers.js @@ -6,11 +6,17 @@ export default class ServersController extends Controller.extend(Sortable) { @alias('model.nodes') nodes; @alias('model.agents') agents; - queryParams = { - currentPage: 'page', - sortProperty: 'sort', - sortDescending: 'desc', - }; + queryParams = [ + { + currentPage: 'page', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; currentPage = 1; pageSize = 8; From e993a71f6f673dbbaee63354d8fbd949433c03e3 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Thu, 11 Jun 2020 16:23:00 -0500 Subject: [PATCH 07/14] Add manually-converted classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I don’t know why the codemod ignored these files 🧐 --- ui/app/adapters/agent.js | 2 +- ui/app/adapters/allocation.js | 16 +- ui/app/adapters/application.js | 2 +- ui/app/adapters/deployment.js | 2 +- ui/app/adapters/job-summary.js | 2 +- ui/app/adapters/job.js | 22 +-- ui/app/adapters/namespace.js | 2 +- ui/app/adapters/node.js | 2 +- ui/app/adapters/plugin.js | 8 +- ui/app/adapters/policy.js | 2 +- ui/app/adapters/token.js | 2 +- ui/app/adapters/volume.js | 8 +- ui/app/components/allocation-status-bar.js | 76 ++++---- ui/app/components/distribution-bar.js | 45 ++--- ui/app/components/exec/task-group-parent.js | 89 +++++----- ui/app/components/line-chart.js | 156 +++++++++------- ui/app/components/page-size-select.js | 12 +- .../allocations/allocation/index.js | 80 +++++---- ui/app/controllers/clients/client.js | 152 +++++++++------- ui/app/controllers/exec.js | 166 +++++++++--------- ui/app/controllers/settings/tokens.js | 105 +++++------ ui/app/routes/application.js | 60 ++++--- ui/app/routes/clients.js | 18 +- ui/app/routes/csi/plugins.js | 12 +- ui/app/routes/csi/volumes.js | 22 +-- ui/app/routes/exec.js | 26 +-- ui/app/routes/jobs.js | 32 ++-- ui/app/routes/jobs/run.js | 22 +-- ui/app/routes/servers.js | 18 +- ui/app/serializers/agent.js | 20 +-- ui/app/serializers/allocation.js | 14 +- ui/app/serializers/deployment.js | 18 +- ui/app/serializers/job-version.js | 19 +- ui/app/serializers/job.js | 16 +- ui/app/serializers/network.js | 12 +- ui/app/serializers/node-event.js | 8 +- ui/app/serializers/node.js | 16 +- ui/app/serializers/resources.js | 8 +- ui/app/serializers/service.js | 12 +- ui/app/serializers/task-event.js | 12 +- ui/app/serializers/token.js | 14 +- ui/app/serializers/volume.js | 22 +-- ui/app/utils/classes/stream-logger.js | 24 ++- ui/app/utils/no-leader-error.js | 6 +- 44 files changed, 740 insertions(+), 642 deletions(-) diff --git a/ui/app/adapters/agent.js b/ui/app/adapters/agent.js index ab259e9ef..8b2d24fa8 100644 --- a/ui/app/adapters/agent.js +++ b/ui/app/adapters/agent.js @@ -1,6 +1,6 @@ import ApplicationAdapter from './application'; -export default class Agent extends ApplicationAdapter { +export default class AgentAdapter extends ApplicationAdapter { pathForType = () => 'agent/members'; urlForFindRecord() { diff --git a/ui/app/adapters/allocation.js b/ui/app/adapters/allocation.js index 93981bc57..20cca3c29 100644 --- a/ui/app/adapters/allocation.js +++ b/ui/app/adapters/allocation.js @@ -1,8 +1,8 @@ import Watchable from './watchable'; import addToPath from 'nomad-ui/utils/add-to-path'; -export default Watchable.extend({ - stop: adapterAction('/stop'), +export default class AllocationAdapter extends Watchable { + stop = adapterAction('/stop'); restart(allocation, taskName) { const prefix = `${this.host || '/'}${this.urlPrefix()}`; @@ -10,22 +10,20 @@ export default Watchable.extend({ return this.ajax(url, 'PUT', { data: taskName && { TaskName: taskName }, }); - }, + } ls(model, path) { return this.token .authorizedRequest(`/v1/client/fs/ls/${model.id}?path=${encodeURIComponent(path)}`) .then(handleFSResponse); - }, + } stat(model, path) { return this.token - .authorizedRequest( - `/v1/client/fs/stat/${model.id}?path=${encodeURIComponent(path)}` - ) + .authorizedRequest(`/v1/client/fs/stat/${model.id}?path=${encodeURIComponent(path)}`) .then(handleFSResponse); - }, -}); + } +} async function handleFSResponse(response) { if (response.ok) { diff --git a/ui/app/adapters/application.js b/ui/app/adapters/application.js index fcc23ed8f..e03cdffe3 100644 --- a/ui/app/adapters/application.js +++ b/ui/app/adapters/application.js @@ -9,7 +9,7 @@ import classic from 'ember-classic-decorator'; export const namespace = 'v1'; @classic -export default class Application extends RESTAdapter { +export default class ApplicationAdapter extends RESTAdapter { namespace = namespace; @service system; diff --git a/ui/app/adapters/deployment.js b/ui/app/adapters/deployment.js index b45f22e56..5530166d6 100644 --- a/ui/app/adapters/deployment.js +++ b/ui/app/adapters/deployment.js @@ -1,6 +1,6 @@ import Watchable from './watchable'; -export default class Deployment extends Watchable { +export default class DeploymentAdapter extends Watchable { promote(deployment) { const id = deployment.get('id'); const url = urlForAction(this.urlForFindRecord(id, 'deployment'), '/promote'); diff --git a/ui/app/adapters/job-summary.js b/ui/app/adapters/job-summary.js index 92a35bb5d..569311c66 100644 --- a/ui/app/adapters/job-summary.js +++ b/ui/app/adapters/job-summary.js @@ -1,6 +1,6 @@ import Watchable from './watchable'; -export default class JobSummary extends Watchable { +export default class JobSummaryAdapter extends Watchable { urlForFindRecord(id, type, hash) { const [name, namespace] = JSON.parse(id); let url = super.urlForFindRecord(name, 'job', hash) + '/summary'; diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index 10b0e1cb2..a052e5c8d 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -1,27 +1,27 @@ import WatchableNamespaceIDs from './watchable-namespace-ids'; import addToPath from 'nomad-ui/utils/add-to-path'; -export default WatchableNamespaceIDs.extend({ - relationshipFallbackLinks: Object.freeze({ +export default class JobAdapter extends WatchableNamespaceIDs { + relationshipFallbackLinks = Object.freeze({ summary: '/summary', - }), + }); fetchRawDefinition(job) { const url = this.urlForFindRecord(job.get('id'), 'job'); return this.ajax(url, 'GET'); - }, + } forcePeriodic(job) { if (job.get('periodic')) { const url = addToPath(this.urlForFindRecord(job.get('id'), 'job'), '/periodic/force'); return this.ajax(url, 'POST'); } - }, + } stop(job) { const url = this.urlForFindRecord(job.get('id'), 'job'); return this.ajax(url, 'DELETE'); - }, + } parse(spec) { const url = addToPath(this.urlForFindAll('job'), '/parse'); @@ -31,7 +31,7 @@ export default WatchableNamespaceIDs.extend({ Canonicalize: true, }, }); - }, + } plan(job) { const jobId = job.get('id') || job.get('_idBeforeSaving'); @@ -48,7 +48,7 @@ export default WatchableNamespaceIDs.extend({ store.pushPayload('job-plan', { jobPlans: [json] }); return store.peekRecord('job-plan', jobId); }); - }, + } // Running a job doesn't follow REST create semantics so it's easier to // treat it as an action. @@ -58,7 +58,7 @@ export default WatchableNamespaceIDs.extend({ Job: job.get('_newDefinitionJSON'), }, }); - }, + } update(job) { const jobId = job.get('id') || job.get('_idBeforeSaving'); @@ -68,5 +68,5 @@ export default WatchableNamespaceIDs.extend({ Job: job.get('_newDefinitionJSON'), }, }); - }, -}); + } +} diff --git a/ui/app/adapters/namespace.js b/ui/app/adapters/namespace.js index 89a004578..365c0933d 100644 --- a/ui/app/adapters/namespace.js +++ b/ui/app/adapters/namespace.js @@ -1,7 +1,7 @@ import ApplicationAdapter from './application'; import codesForError from '../utils/codes-for-error'; -export default class Namespace extends ApplicationAdapter { +export default class NamespaceAdapter extends ApplicationAdapter { findRecord(store, modelClass, id) { return super.findRecord(...arguments).catch(error => { const errorCodes = codesForError(error); diff --git a/ui/app/adapters/node.js b/ui/app/adapters/node.js index cde6ef79f..746f51f6e 100644 --- a/ui/app/adapters/node.js +++ b/ui/app/adapters/node.js @@ -1,7 +1,7 @@ import Watchable from './watchable'; import addToPath from 'nomad-ui/utils/add-to-path'; -export default class Node extends Watchable { +export default class NodeAdapter extends Watchable { setEligible(node) { return this.setEligibility(node, true); } diff --git a/ui/app/adapters/plugin.js b/ui/app/adapters/plugin.js index 836488ad1..ff304dbdf 100644 --- a/ui/app/adapters/plugin.js +++ b/ui/app/adapters/plugin.js @@ -1,7 +1,7 @@ import Watchable from './watchable'; -export default Watchable.extend({ - queryParamsToAttrs: Object.freeze({ +export default class PluginAdapter extends Watchable { + queryParamsToAttrs = Object.freeze({ type: 'type', - }), -}); + }); +} diff --git a/ui/app/adapters/policy.js b/ui/app/adapters/policy.js index e21e301e7..79e68bcc4 100644 --- a/ui/app/adapters/policy.js +++ b/ui/app/adapters/policy.js @@ -1,5 +1,5 @@ import { default as ApplicationAdapter, namespace } from './application'; -export default class Policy extends ApplicationAdapter { +export default class PolicyAdapter extends ApplicationAdapter { namespace = namespace + '/acl'; } diff --git a/ui/app/adapters/token.js b/ui/app/adapters/token.js index b0a149dc0..771d101d9 100644 --- a/ui/app/adapters/token.js +++ b/ui/app/adapters/token.js @@ -1,7 +1,7 @@ import { inject as service } from '@ember/service'; import { default as ApplicationAdapter, namespace } from './application'; -export default class Token extends ApplicationAdapter { +export default class TokenAdapter extends ApplicationAdapter { @service store; namespace = namespace + '/acl'; diff --git a/ui/app/adapters/volume.js b/ui/app/adapters/volume.js index b6076bf76..fbc665b32 100644 --- a/ui/app/adapters/volume.js +++ b/ui/app/adapters/volume.js @@ -1,8 +1,8 @@ import WatchableNamespaceIDs from './watchable-namespace-ids'; -export default WatchableNamespaceIDs.extend({ - queryParamsToAttrs: Object.freeze({ +export default class VolumeAdapter extends WatchableNamespaceIDs { + queryParamsToAttrs = Object.freeze({ type: 'type', plugin_id: 'plugin.id', - }), -}); + }); +} diff --git a/ui/app/components/allocation-status-bar.js b/ui/app/components/allocation-status-bar.js index 6e3046570..803884b6d 100644 --- a/ui/app/components/allocation-status-bar.js +++ b/ui/app/components/allocation-status-bar.js @@ -1,45 +1,45 @@ import { computed } from '@ember/object'; import DistributionBar from './distribution-bar'; -export default DistributionBar.extend({ - layoutName: 'components/distribution-bar', +export default class AllocationStatusBar extends DistributionBar { + layoutName = 'components/distribution-bar'; - allocationContainer: null, + allocationContainer = null; - 'data-test-allocation-status-bar': true, + 'data-test-allocation-status-bar' = true; - data: computed( - 'allocationContainer.{queuedAllocs,completeAllocs,failedAllocs,runningAllocs,startingAllocs}', - function() { - if (!this.allocationContainer) { - return []; - } - - const allocs = this.allocationContainer.getProperties( - 'queuedAllocs', - 'completeAllocs', - 'failedAllocs', - 'runningAllocs', - 'startingAllocs', - 'lostAllocs' - ); - return [ - { label: 'Queued', value: allocs.queuedAllocs, className: 'queued' }, - { - label: 'Starting', - value: allocs.startingAllocs, - className: 'starting', - layers: 2, - }, - { label: 'Running', value: allocs.runningAllocs, className: 'running' }, - { - label: 'Complete', - value: allocs.completeAllocs, - className: 'complete', - }, - { label: 'Failed', value: allocs.failedAllocs, className: 'failed' }, - { label: 'Lost', value: allocs.lostAllocs, className: 'lost' }, - ]; + @computed( + 'allocationContainer.{queuedAllocs,completeAllocs,failedAllocs,runningAllocs,startingAllocs}' + ) + get data() { + if (!this.allocationContainer) { + return []; } - ), -}); + + const allocs = this.allocationContainer.getProperties( + 'queuedAllocs', + 'completeAllocs', + 'failedAllocs', + 'runningAllocs', + 'startingAllocs', + 'lostAllocs' + ); + return [ + { label: 'Queued', value: allocs.queuedAllocs, className: 'queued' }, + { + label: 'Starting', + value: allocs.startingAllocs, + className: 'starting', + layers: 2, + }, + { label: 'Running', value: allocs.runningAllocs, className: 'running' }, + { + label: 'Complete', + value: allocs.completeAllocs, + className: 'complete', + }, + { label: 'Failed', value: allocs.failedAllocs, className: 'failed' }, + { label: 'Lost', value: allocs.lostAllocs, className: 'lost' }, + ]; + } +} diff --git a/ui/app/components/distribution-bar.js b/ui/app/components/distribution-bar.js index 743aead63..271fa6806 100644 --- a/ui/app/components/distribution-bar.js +++ b/ui/app/components/distribution-bar.js @@ -1,6 +1,7 @@ /* eslint-disable ember/no-observers */ import Component from '@ember/component'; -import { computed, observer, set } from '@ember/object'; +import { computed, set } from '@ember/object'; +import { observes } from '@ember-decorators/object'; import { run } from '@ember/runloop'; import { assign } from '@ember/polyfills'; import { guidFor } from '@ember/object/internals'; @@ -9,22 +10,25 @@ import d3 from 'd3-selection'; import 'd3-transition'; import WindowResizable from '../mixins/window-resizable'; import styleStringProperty from '../utils/properties/style-string'; +import { classNames, classNameBindings } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; const sumAggregate = (total, val) => total + val; -export default Component.extend(WindowResizable, { - classNames: ['chart', 'distribution-bar'], - classNameBindings: ['isNarrow:is-narrow'], +@classic +@classNames('chart', 'distribution-bar') +@classNameBindings('isNarrow:is-narrow') +export default class DistributionBar extends Component.extend(WindowResizable) { + chart = null; + data = null; + activeDatum = null; + isNarrow = false; - chart: null, - data: null, - activeDatum: null, - isNarrow: false, + @styleStringProperty('tooltipPosition') tooltipStyle; + maskId = null; - tooltipStyle: styleStringProperty('tooltipPosition'), - maskId: null, - - _data: computed('data', function() { + @computed('data') + get _data() { const data = copy(this.data, true); const sum = data.mapBy('value').reduce(sumAggregate, 0); @@ -41,7 +45,7 @@ export default Component.extend(WindowResizable, { .mapBy('value') .reduce(sumAggregate, 0) / sum, })); - }), + } didInsertElement() { const svg = this.element.querySelector('svg'); @@ -63,15 +67,16 @@ export default Component.extend(WindowResizable, { }); this.renderChart(); - }, + } didUpdateAttrs() { this.renderChart(); - }, + } - updateChart: observer('_data.@each.{value,label,className}', function() { + @observes('_data.@each.{value,label,className}') + updateChart() { this.renderChart(); - }), + } // prettier-ignore /* eslint-disable */ @@ -166,10 +171,10 @@ export default Component.extend(WindowResizable, { .attr('height', '6px') .attr('y', '50%'); } - }, + } /* eslint-enable */ windowResizeHandler() { run.once(this, this.renderChart); - }, -}); + } +} diff --git a/ui/app/components/exec/task-group-parent.js b/ui/app/components/exec/task-group-parent.js index 2eced1676..f56b7ecb1 100644 --- a/ui/app/components/exec/task-group-parent.js +++ b/ui/app/components/exec/task-group-parent.js @@ -1,16 +1,19 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import { filterBy, mapBy, or, sort } from '@ember/object/computed'; import generateExecUrl from 'nomad-ui/utils/generate-exec-url'; import openExecUrl from 'nomad-ui/utils/open-exec-url'; +import classic from 'ember-classic-decorator'; -export default Component.extend({ - router: service(), +@classic +export default class TaskGroupParent extends Component { + @service router; - isOpen: or('clickedOpen', 'currentRouteIsThisTaskGroup'), + @or('clickedOpen', 'currentRouteIsThisTaskGroup') isOpen; - currentRouteIsThisTaskGroup: computed('router.currentRoute', function() { + @computed('router.currentRoute') + get currentRouteIsThisTaskGroup() { const route = this.router.currentRoute; if (route.name.includes('task-group')) { @@ -24,58 +27,60 @@ export default Component.extend({ } else { return false; } - }), + } - hasPendingAllocations: computed('taskGroup.allocations.@each.clientStatus', function() { + @computed('taskGroup.allocations.@each.clientStatus') + get hasPendingAllocations() { return this.taskGroup.allocations.any(allocation => allocation.clientStatus === 'pending'); - }), + } - allocationTaskStatesRecordArrays: mapBy('taskGroup.allocations', 'states'), - allocationTaskStates: computed('allocationTaskStatesRecordArrays.[]', function() { + @mapBy('taskGroup.allocations', 'states') allocationTaskStatesRecordArrays; + @computed('allocationTaskStatesRecordArrays.[]') + get allocationTaskStates() { const flattenRecordArrays = (accumulator, recordArray) => accumulator.concat(recordArray.toArray()); return this.allocationTaskStatesRecordArrays.reduce(flattenRecordArrays, []); - }), + } - activeTaskStates: filterBy('allocationTaskStates', 'isActive'), + @filterBy('allocationTaskStates', 'isActive') activeTaskStates; - activeTasks: mapBy('activeTaskStates', 'task'), - activeTaskGroups: mapBy('activeTasks', 'taskGroup'), + @mapBy('activeTaskStates', 'task') activeTasks; + @mapBy('activeTasks', 'taskGroup') activeTaskGroups; - tasksWithRunningStates: computed( + @computed( 'taskGroup.name', 'activeTaskStates.@each.name', 'activeTasks.@each.name', - 'activeTaskGroups.@each.name', - function() { - const activeTaskStateNames = this.activeTaskStates - .filter(taskState => { - return taskState.task && taskState.task.taskGroup.name === this.taskGroup.name; - }) - .mapBy('name'); + 'activeTaskGroups.@each.name' + ) + get tasksWithRunningStates() { + const activeTaskStateNames = this.activeTaskStates + .filter(taskState => { + return taskState.task && taskState.task.taskGroup.name === this.taskGroup.name; + }) + .mapBy('name'); - return this.taskGroup.tasks.filter(task => activeTaskStateNames.includes(task.name)); - } - ), + return this.taskGroup.tasks.filter(task => activeTaskStateNames.includes(task.name)); + } - taskSorting: Object.freeze(['name']), - sortedTasks: sort('tasksWithRunningStates', 'taskSorting'), + taskSorting = Object.freeze(['name']); + @sort('tasksWithRunningStates', 'taskSorting') sortedTasks; - clickedOpen: false, + clickedOpen = false; - actions: { - toggleOpen() { - this.toggleProperty('clickedOpen'); - }, + @action + toggleOpen() { + this.toggleProperty('clickedOpen'); + } - openInNewWindow(job, taskGroup, task) { - let url = generateExecUrl(this.router, { - job, - taskGroup, - task, - }); + @action + openInNewWindow(job, taskGroup, task) { + let url = generateExecUrl(this.router, { + job, + taskGroup, + task, + }); - openExecUrl(url); - }, - }, -}); + openExecUrl(url); + } +} diff --git a/ui/app/components/line-chart.js b/ui/app/components/line-chart.js index d0b282db6..2dd5e6cee 100644 --- a/ui/app/components/line-chart.js +++ b/ui/app/components/line-chart.js @@ -1,6 +1,7 @@ /* eslint-disable ember/no-observers */ import Component from '@ember/component'; -import { computed, observer } from '@ember/object'; +import { computed } from '@ember/object'; +import { observes } from '@ember-decorators/object'; import { computed as overridable } from 'ember-overridable-computed'; import { guidFor } from '@ember/object/internals'; import { run } from '@ember/runloop'; @@ -13,6 +14,8 @@ import d3Format from 'd3-format'; import d3TimeFormat from 'd3-time-format'; import WindowResizable from 'nomad-ui/mixins/window-resizable'; import styleStringProperty from 'nomad-ui/utils/properties/style-string'; +import { classNames } from '@ember-decorators/component'; +import classic from 'ember-classic-decorator'; // Returns a new array with the specified number of points linearly // distributed across the bounds @@ -28,68 +31,73 @@ const lerp = ([low, high], numPoints) => { // Round a number or an array of numbers const nice = val => (val instanceof Array ? val.map(nice) : Math.round(val)); -export default Component.extend(WindowResizable, { - classNames: ['chart', 'line-chart'], - +@classic +@classNames('chart', 'line-chart') +export default class LineChart extends Component.extend(WindowResizable) { // Public API - data: null, - xProp: null, - yProp: null, - timeseries: false, - chartClass: 'is-primary', + data = null; + xProp = null; + yProp = null; + timeseries = false; + chartClass = 'is-primary'; - title: 'Line Chart', - description: null, + title = 'Line Chart'; + description = null; // Private Properties - width: 0, - height: 0, + width = 0; + height = 0; - isActive: false, + isActive = false; - fillId: computed(function() { + @computed() + get fillId() { return `line-chart-fill-${guidFor(this)}`; - }), + } - maskId: computed(function() { + @computed() + get maskId() { return `line-chart-mask-${guidFor(this)}`; - }), + } - activeDatum: null, + activeDatum = null; - activeDatumLabel: computed('activeDatum', function() { + @computed('activeDatum') + get activeDatumLabel() { const datum = this.activeDatum; - if (!datum) return; + if (!datum) return undefined; const x = datum[this.xProp]; return this.xFormat(this.timeseries)(x); - }), + } - activeDatumValue: computed('activeDatum', function() { + @computed('activeDatum') + get activeDatumValue() { const datum = this.activeDatum; - if (!datum) return; + if (!datum) return undefined; const y = datum[this.yProp]; return this.yFormat()(y); - }), + } // Overridable functions that retrurn formatter functions xFormat(timeseries) { return timeseries ? d3TimeFormat.timeFormat('%b') : d3Format.format(','); - }, + } yFormat() { return d3Format.format(',.2~r'); - }, + } - tooltipPosition: null, - tooltipStyle: styleStringProperty('tooltipPosition'), + tooltipPosition = null; + @styleStringProperty('tooltipPosition') tooltipStyle; - xScale: computed('data.[]', 'xProp', 'timeseries', 'yAxisOffset', function() { + @computed('data.[]', 'xProp', 'timeseries', 'yAxisOffset') + get xScale() { const xProp = this.xProp; const scale = this.timeseries ? d3Scale.scaleTime() : d3Scale.scaleLinear(); const data = this.data; @@ -99,25 +107,28 @@ export default Component.extend(WindowResizable, { scale.rangeRound([10, this.yAxisOffset]).domain(domain); return scale; - }), + } - xRange: computed('data.[]', 'xFormat', 'xProp', 'timeseries', function() { + @computed('data.[]', 'xFormat', 'xProp', 'timeseries') + get xRange() { const { xProp, timeseries, data } = this; const range = d3Array.extent(data, d => d[xProp]); const formatter = this.xFormat(timeseries); return range.map(formatter); - }), + } - yRange: computed('data.[]', 'yFormat', 'yProp', function() { + @computed('data.[]', 'yFormat', 'yProp') + get yRange() { const yProp = this.yProp; const range = d3Array.extent(this.data, d => d[yProp]); const formatter = this.yFormat(); return range.map(formatter); - }), + } - yScale: computed('data.[]', 'yProp', 'xAxisOffset', function() { + @computed('data.[]', 'yProp', 'xAxisOffset') + get yScale() { const yProp = this.yProp; let max = d3Array.max(this.data, d => d[yProp]) || 1; if (max > 1) { @@ -128,9 +139,10 @@ export default Component.extend(WindowResizable, { .scaleLinear() .rangeRound([this.xAxisOffset, 10]) .domain([0, max]); - }), + } - xAxis: computed('xScale', function() { + @computed('xScale') + get xAxis() { const formatter = this.xFormat(this.timeseries); return d3Axis @@ -138,17 +150,19 @@ export default Component.extend(WindowResizable, { .scale(this.xScale) .ticks(5) .tickFormat(formatter); - }), + } - yTicks: computed('xAxisOffset', function() { + @computed('xAxisOffset') + get yTicks() { const height = this.xAxisOffset; const tickCount = Math.ceil(height / 120) * 2 + 1; const domain = this.yScale.domain(); const ticks = lerp(domain, tickCount); return domain[1] - domain[0] > 1 ? nice(ticks) : ticks; - }), + } - yAxis: computed('yScale', function() { + @computed('yScale') + get yAxis() { const formatter = this.yFormat(); return d3Axis @@ -156,9 +170,10 @@ export default Component.extend(WindowResizable, { .scale(this.yScale) .tickValues(this.yTicks) .tickFormat(formatter); - }), + } - yGridlines: computed('yScale', function() { + @computed('yScale') + get yGridlines() { // The first gridline overlaps the x-axis, so remove it const [, ...ticks] = this.yTicks; @@ -168,33 +183,38 @@ export default Component.extend(WindowResizable, { .tickValues(ticks) .tickSize(-this.yAxisOffset) .tickFormat(''); - }), + } - xAxisHeight: computed(function() { + @computed() + get xAxisHeight() { // Avoid divide by zero errors by always having a height if (!this.element) return 1; const axis = this.element.querySelector('.x-axis'); return axis && axis.getBBox().height; - }), + } - yAxisWidth: computed(function() { + @computed() + get yAxisWidth() { // Avoid divide by zero errors by always having a width if (!this.element) return 1; const axis = this.element.querySelector('.y-axis'); return axis && axis.getBBox().width; - }), + } - xAxisOffset: overridable('height', 'xAxisHeight', function() { + @overridable('height', 'xAxisHeight', function() { return this.height - this.xAxisHeight; - }), + }) + xAxisOffset; - yAxisOffset: computed('width', 'yAxisWidth', function() { + @computed('width', 'yAxisWidth') + get yAxisOffset() { return this.width - this.yAxisWidth; - }), + } - line: computed('data.[]', 'xScale', 'yScale', function() { + @computed('data.[]', 'xScale', 'yScale') + get line() { const { xScale, yScale, xProp, yProp } = this; const line = d3Shape @@ -204,9 +224,10 @@ export default Component.extend(WindowResizable, { .y(d => yScale(d[yProp])); return line(this.data); - }), + } - area: computed('data.[]', 'xScale', 'yScale', function() { + @computed('data.[]', 'xScale', 'yScale') + get area() { const { xScale, yScale, xProp, yProp } = this; const area = d3Shape @@ -217,7 +238,7 @@ export default Component.extend(WindowResizable, { .y1(d => yScale(d[yProp])); return area(this.data); - }), + } didInsertElement() { this.updateDimensions(); @@ -243,11 +264,11 @@ export default Component.extend(WindowResizable, { run.schedule('afterRender', this, () => this.set('isActive', false)); this.set('activeDatum', null); }); - }, + } didUpdateAttrs() { this.renderChart(); - }, + } updateActiveDatum(mouseX) { const { xScale, xProp, yScale, yProp, data } = this; @@ -278,11 +299,12 @@ export default Component.extend(WindowResizable, { left: xScale(datum[xProp]), top: yScale(datum[yProp]) - 10, }); - }, + } - updateChart: observer('data.[]', function() { + @observes('data.[]') + updateChart() { this.renderChart(); - }), + } // The renderChart method should only ever be responsible for runtime calculations // and appending d3 created elements to the DOM (such as axes). @@ -308,7 +330,7 @@ export default Component.extend(WindowResizable, { this.updateActiveDatum(this.latestMouseX); } }); - }, + } mountD3Elements() { if (!this.isDestroyed && !this.isDestroying) { @@ -316,11 +338,11 @@ export default Component.extend(WindowResizable, { d3.select(this.element.querySelector('.y-axis')).call(this.yAxis); d3.select(this.element.querySelector('.y-gridlines')).call(this.yGridlines); } - }, + } windowResizeHandler() { run.once(this, this.updateDimensions); - }, + } updateDimensions() { const $svg = this.element.querySelector('svg'); @@ -329,5 +351,5 @@ export default Component.extend(WindowResizable, { this.setProperties({ width, height }); this.renderChart(); - }, -}); + } +} diff --git a/ui/app/components/page-size-select.js b/ui/app/components/page-size-select.js index 0776d7084..3c6c3f0b3 100644 --- a/ui/app/components/page-size-select.js +++ b/ui/app/components/page-size-select.js @@ -1,11 +1,11 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; -export default Component.extend({ - userSettings: service(), +export default class PageSizeSelect extends Component { + @service userSettings; - tagName: '', - pageSizeOptions: Object.freeze([10, 25, 50]), + tagName = ''; + pageSizeOptions = Object.freeze([10, 25, 50]); - onChange() {}, -}); + onChange() {} +} diff --git a/ui/app/controllers/allocations/allocation/index.js b/ui/app/controllers/allocations/allocation/index.js index ebccdfa75..e167cdbe6 100644 --- a/ui/app/controllers/allocations/allocation/index.js +++ b/ui/app/controllers/allocations/allocation/index.js @@ -1,58 +1,68 @@ /* eslint-disable ember/no-observers */ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; -import { computed, observer } from '@ember/object'; +import { action, computed } from '@ember/object'; +import { observes } from '@ember-decorators/object'; import { computed as overridable } from 'ember-overridable-computed'; import { alias } from '@ember/object/computed'; import { task } from 'ember-concurrency'; import Sortable from 'nomad-ui/mixins/sortable'; import { lazyClick } from 'nomad-ui/helpers/lazy-click'; import { watchRecord } from 'nomad-ui/utils/properties/watch'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(Sortable, { - token: service(), +@classic +export default class IndexController extends Controller.extend(Sortable) { + @service token; - queryParams: { - sortProperty: 'sort', - sortDescending: 'desc', - }, + queryParams = [ + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; - sortProperty: 'name', - sortDescending: false, + sortProperty = 'name'; + sortDescending = false; - listToSort: alias('model.states'), - sortedStates: alias('listSorted'), + @alias('model.states') listToSort; + @alias('listSorted') sortedStates; // Set in the route - preempter: null, + preempter = null; - error: overridable(() => { + @overridable(function() { // { title, description } return null; - }), + }) + error; - network: alias('model.allocatedResources.networks.firstObject'), + @alias('model.allocatedResources.networks.firstObject') network; - services: computed('model.taskGroup.services.@each.name', function() { + @computed('model.taskGroup.services.@each.name') + get services() { return this.get('model.taskGroup.services').sortBy('name'); - }), + } onDismiss() { this.set('error', null); - }, + } - watchNext: watchRecord('allocation'), + @watchRecord('allocation') watchNext; - observeWatchNext: observer('model.nextAllocation.clientStatus', function() { + @observes('model.nextAllocation.clientStatus') + observeWatchNext() { const nextAllocation = this.model.nextAllocation; if (nextAllocation && nextAllocation.content) { this.watchNext.perform(nextAllocation); } else { this.watchNext.cancelAll(); } - }), + } - stopAllocation: task(function*() { + @task(function*() { try { yield this.model.stop(); // Eagerly update the allocation clientStatus to avoid flickering @@ -63,9 +73,10 @@ export default Controller.extend(Sortable, { description: 'Your ACL token does not grant allocation lifecycle permissions.', }); } - }), + }) + stopAllocation; - restartAllocation: task(function*() { + @task(function*() { try { yield this.model.restart(); } catch (err) { @@ -74,15 +85,16 @@ export default Controller.extend(Sortable, { description: 'Your ACL token does not grant allocation lifecycle permissions.', }); } - }), + }) + restartAllocation; - actions: { - gotoTask(allocation, task) { - this.transitionToRoute('allocations.allocation.task', task); - }, + @action + gotoTask(allocation, task) { + this.transitionToRoute('allocations.allocation.task', task); + } - taskClick(allocation, task, event) { - lazyClick([() => this.send('gotoTask', allocation, task), event]); - }, - }, -}); + @action + taskClick(allocation, task, event) { + lazyClick([() => this.send('gotoTask', allocation, task), event]); + } +} diff --git a/ui/app/controllers/clients/client.js b/ui/app/controllers/clients/client.js index 260fdc4fa..e1858f089 100644 --- a/ui/app/controllers/clients/client.js +++ b/ui/app/controllers/clients/client.js @@ -1,84 +1,99 @@ /* eslint-disable ember/no-observers */ import { alias } from '@ember/object/computed'; import Controller from '@ember/controller'; -import { computed, observer } from '@ember/object'; +import { action, computed } from '@ember/object'; +import { observes } from '@ember-decorators/object'; import { task } from 'ember-concurrency'; import Sortable from 'nomad-ui/mixins/sortable'; import Searchable from 'nomad-ui/mixins/searchable'; import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error'; +import classic from 'ember-classic-decorator'; -export default Controller.extend(Sortable, Searchable, { - queryParams: { - currentPage: 'page', - searchTerm: 'search', - sortProperty: 'sort', - sortDescending: 'desc', - onlyPreemptions: 'preemptions', - }, +@classic +export default class ClientController extends Controller.extend(Sortable, Searchable) { + queryParams = [ + { + currentPage: 'page', + }, + { + searchTerm: 'search', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + { + onlyPreemptions: 'preemptions', + }, + ]; // Set in the route - flagAsDraining: false, + flagAsDraining = false; - currentPage: 1, - pageSize: 8, + currentPage = 1; + pageSize = 8; - sortProperty: 'modifyIndex', - sortDescending: true, + sortProperty = 'modifyIndex'; + sortDescending = true; - searchProps: computed(function() { + @computed() + get searchProps() { return ['shortId', 'name']; - }), + } - onlyPreemptions: false, + onlyPreemptions = false; - visibleAllocations: computed( - 'model.allocations.[]', - 'preemptions.[]', - 'onlyPreemptions', - function() { - return this.onlyPreemptions ? this.preemptions : this.model.allocations; - } - ), + @computed('model.allocations.[]', 'preemptions.[]', 'onlyPreemptions') + get visibleAllocations() { + return this.onlyPreemptions ? this.preemptions : this.model.allocations; + } - listToSort: alias('visibleAllocations'), - listToSearch: alias('listSorted'), - sortedAllocations: alias('listSearched'), + @alias('visibleAllocations') listToSort; + @alias('listSorted') listToSearch; + @alias('listSearched') sortedAllocations; - eligibilityError: null, - stopDrainError: null, - drainError: null, - showDrainNotification: false, - showDrainUpdateNotification: false, - showDrainStoppedNotification: false, + eligibilityError = null; + stopDrainError = null; + drainError = null; + showDrainNotification = false; + showDrainUpdateNotification = false; + showDrainStoppedNotification = false; - preemptions: computed('model.allocations.@each.wasPreempted', function() { + @computed('model.allocations.@each.wasPreempted') + get preemptions() { return this.model.allocations.filterBy('wasPreempted'); - }), + } - sortedEvents: computed('model.events.@each.time', function() { + @computed('model.events.@each.time') + get sortedEvents() { return this.get('model.events') .sortBy('time') .reverse(); - }), + } - sortedDrivers: computed('model.drivers.@each.name', function() { + @computed('model.drivers.@each.name') + get sortedDrivers() { return this.get('model.drivers').sortBy('name'); - }), + } - sortedHostVolumes: computed('model.hostVolumes.@each.name', function() { + @computed('model.hostVolumes.@each.name') + get sortedHostVolumes() { return this.model.hostVolumes.sortBy('name'); - }), + } - setEligibility: task(function*(value) { + @(task(function*(value) { try { yield value ? this.model.setEligible() : this.model.setIneligible(); } catch (err) { const error = messageFromAdapterError(err) || 'Could not set eligibility'; this.set('eligibilityError', error); } - }).drop(), + }).drop()) + setEligibility; - stopDrain: task(function*() { + @(task(function*() { try { this.set('flagAsDraining', false); yield this.model.cancelDrain(); @@ -88,9 +103,10 @@ export default Controller.extend(Sortable, Searchable, { const error = messageFromAdapterError(err) || 'Could not stop drain'; this.set('stopDrainError', error); } - }).drop(), + }).drop()) + stopDrain; - forceDrain: task(function*() { + @(task(function*() { try { yield this.model.forceDrain({ IgnoreSystemJobs: this.model.drainStrategy.ignoreSystemJobs, @@ -99,32 +115,36 @@ export default Controller.extend(Sortable, Searchable, { const error = messageFromAdapterError(err) || 'Could not force drain'; this.set('drainError', error); } - }).drop(), + }).drop()) + forceDrain; - triggerDrainNotification: observer('model.isDraining', function() { + @observes('model.isDraining') + triggerDrainNotification() { if (!this.model.isDraining && this.flagAsDraining) { this.set('showDrainNotification', true); } this.set('flagAsDraining', this.model.isDraining); - }), + } - actions: { - gotoAllocation(allocation) { - this.transitionToRoute('allocations.allocation', allocation); - }, + @action + gotoAllocation(allocation) { + this.transitionToRoute('allocations.allocation', allocation); + } - setPreemptionFilter(value) { - this.set('onlyPreemptions', value); - }, + @action + setPreemptionFilter(value) { + this.set('onlyPreemptions', value); + } - drainNotify(isUpdating) { - this.set('showDrainUpdateNotification', isUpdating); - }, + @action + drainNotify(isUpdating) { + this.set('showDrainUpdateNotification', isUpdating); + } - drainError(err) { - const error = messageFromAdapterError(err) || 'Could not run drain'; - this.set('drainError', error); - }, - }, -}); + @action + drainError(err) { + const error = messageFromAdapterError(err) || 'Could not run drain'; + this.set('drainError', error); + } +} diff --git a/ui/app/controllers/exec.js b/ui/app/controllers/exec.js index d6ea565fe..d648753fc 100644 --- a/ui/app/controllers/exec.js +++ b/ui/app/controllers/exec.js @@ -1,36 +1,39 @@ import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; -import { computed } from '@ember/object'; +import { action, computed } from '@ember/object'; import { alias, mapBy, sort, uniq } from '@ember/object/computed'; import escapeTaskName from 'nomad-ui/utils/escape-task-name'; import ExecCommandEditorXtermAdapter from 'nomad-ui/utils/classes/exec-command-editor-xterm-adapter'; import ExecSocketXtermAdapter from 'nomad-ui/utils/classes/exec-socket-xterm-adapter'; import localStorageProperty from 'nomad-ui/utils/properties/local-storage'; +import classic from 'ember-classic-decorator'; const ANSI_UI_GRAY_400 = '\x1b[38;2;142;150;163m'; const ANSI_WHITE = '\x1b[0m'; -export default Controller.extend({ - sockets: service(), - system: service(), - token: service(), +@classic +export default class ExecController extends Controller { + @service sockets; + @service system; + @service token; - queryParams: ['allocation'], + queryParams = ['allocation']; - command: localStorageProperty('nomadExecCommand', '/bin/bash'), - socketOpen: false, + @localStorageProperty('nomadExecCommand', '/bin/bash') command; + socketOpen = false; - pendingAndRunningAllocations: computed('model.allocations.@each.clientStatus', function() { + @computed('model.allocations.@each.clientStatus') + get pendingAndRunningAllocations() { return this.model.allocations.filter( allocation => allocation.clientStatus === 'pending' || allocation.clientStatus === 'running' ); - }), + } - pendingAndRunningTaskGroups: mapBy('pendingAndRunningAllocations', 'taskGroup'), - uniquePendingAndRunningTaskGroups: uniq('pendingAndRunningTaskGroups'), + @mapBy('pendingAndRunningAllocations', 'taskGroup') pendingAndRunningTaskGroups; + @uniq('pendingAndRunningTaskGroups') uniquePendingAndRunningTaskGroups; - taskGroupSorting: Object.freeze(['name']), - sortedTaskGroups: sort('uniquePendingAndRunningTaskGroups', 'taskGroupSorting'), + taskGroupSorting = Object.freeze(['name']); + @sort('uniquePendingAndRunningTaskGroups', 'taskGroupSorting') sortedTaskGroups; setUpTerminal(Terminal) { this.terminal = new Terminal({ fontFamily: 'monospace', fontWeight: '400' }); @@ -38,86 +41,85 @@ export default Controller.extend({ this.terminal.write(ANSI_UI_GRAY_400); this.terminal.writeln('Select a task to start your session.'); - }, + } - allocations: alias('model.allocations'), + @alias('model.allocations') allocations; - taskState: computed( + @computed( 'allocations.{[],@each.isActive}', 'allocationShortId', 'taskName', 'taskGroupName', 'allocation', - 'allocation.states.@each.{name,isRunning}', - function() { - if (!this.allocations) { - return false; - } - - let allocation; - - if (this.allocationShortId) { - allocation = this.allocations.findBy('shortId', this.allocationShortId); - } else { - allocation = this.allocations.find(allocation => - allocation.states - .filterBy('isActive') - .mapBy('name') - .includes(this.taskName) - ); - } - - if (allocation) { - return allocation.states.find(state => state.name === this.taskName); - } - - return; + 'allocation.states.@each.{name,isRunning}' + ) + get taskState() { + if (!this.allocations) { + return false; } - ), - actions: { - setTaskProperties({ allocationShortId, taskName, taskGroupName }) { - this.setProperties({ - allocationShortId, - taskName, - taskGroupName, - }); + let allocation; - if (this.taskState) { - this.terminal.write(ANSI_UI_GRAY_400); - this.terminal.writeln(''); + if (this.allocationShortId) { + allocation = this.allocations.findBy('shortId', this.allocationShortId); + } else { + allocation = this.allocations.find(allocation => + allocation.states + .filterBy('isActive') + .mapBy('name') + .includes(this.taskName) + ); + } - if (!allocationShortId) { - this.terminal.writeln( - 'Multiple instances of this task are running. The allocation below was selected by random draw.' - ); - this.terminal.writeln(''); - } + if (allocation) { + return allocation.states.find(state => state.name === this.taskName); + } - this.terminal.writeln('Customize your command, then hit ‘return’ to run.'); - this.terminal.writeln(''); - this.terminal.write( - `$ nomad alloc exec -i -t -task ${escapeTaskName(taskName)} ${ - this.taskState.allocation.shortId - } ` - ); - - this.terminal.write(ANSI_WHITE); - - this.terminal.write(this.command); - - if (this.commandEditorAdapter) { - this.commandEditorAdapter.destroy(); - } - - this.commandEditorAdapter = new ExecCommandEditorXtermAdapter( - this.terminal, - this.openAndConnectSocket.bind(this), - this.command + return undefined; + } + + @action + setTaskProperties({ allocationShortId, taskName, taskGroupName }) { + this.setProperties({ + allocationShortId, + taskName, + taskGroupName, + }); + + if (this.taskState) { + this.terminal.write(ANSI_UI_GRAY_400); + this.terminal.writeln(''); + + if (!allocationShortId) { + this.terminal.writeln( + 'Multiple instances of this task are running. The allocation below was selected by random draw.' ); + this.terminal.writeln(''); } - }, - }, + + this.terminal.writeln('Customize your command, then hit ‘return’ to run.'); + this.terminal.writeln(''); + this.terminal.write( + `$ nomad alloc exec -i -t -task ${escapeTaskName(taskName)} ${ + this.taskState.allocation.shortId + } ` + ); + + this.terminal.write(ANSI_WHITE); + + this.terminal.write(this.command); + + if (this.commandEditorAdapter) { + this.commandEditorAdapter.destroy(); + } + + this.commandEditorAdapter = new ExecCommandEditorXtermAdapter( + this.terminal, + this.openAndConnectSocket.bind(this), + this.command + ); + } + } openAndConnectSocket(command) { if (this.taskState) { @@ -129,5 +131,5 @@ export default Controller.extend({ } else { this.terminal.writeln(`Failed to open a socket because task ${this.taskName} is not active.`); } - }, -}); + } +} diff --git a/ui/app/controllers/settings/tokens.js b/ui/app/controllers/settings/tokens.js index 8caab695f..956268374 100644 --- a/ui/app/controllers/settings/tokens.js +++ b/ui/app/controllers/settings/tokens.js @@ -3,66 +3,69 @@ import { reads } from '@ember/object/computed'; import Controller from '@ember/controller'; import { getOwner } from '@ember/application'; import { alias } from '@ember/object/computed'; +import { action } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default Controller.extend({ - token: service(), - system: service(), - store: service(), +@classic +export default class Tokens extends Controller { + @service token; + @service system; + @service store; - secret: reads('token.secret'), + @reads('token.secret') secret; - tokenIsValid: false, - tokenIsInvalid: false, - tokenRecord: alias('token.selfToken'), + tokenIsValid = false; + tokenIsInvalid = false; + @alias('token.selfToken') tokenRecord; resetStore() { this.store.unloadAll(); - }, + } - actions: { - clearTokenProperties() { - this.token.setProperties({ - secret: undefined, - }); - this.setProperties({ - tokenIsValid: false, - tokenIsInvalid: false, - }); - this.resetStore(); - this.token.reset(); - }, + @action + clearTokenProperties() { + this.token.setProperties({ + secret: undefined, + }); + this.setProperties({ + tokenIsValid: false, + tokenIsInvalid: false, + }); + this.resetStore(); + this.token.reset(); + } - verifyToken() { - const { secret } = this; - const TokenAdapter = getOwner(this).lookup('adapter:token'); + @action + verifyToken() { + const { secret } = this; + const TokenAdapter = getOwner(this).lookup('adapter:token'); - this.set('token.secret', secret); + this.set('token.secret', secret); - TokenAdapter.findSelf().then( - () => { - // Clear out all data to ensure only data the new token is privileged to - // see is shown - this.system.reset(); - this.resetStore(); + TokenAdapter.findSelf().then( + () => { + // Clear out all data to ensure only data the new token is privileged to + // see is shown + this.system.reset(); + this.resetStore(); - // Refetch the token and associated policies - this.get('token.fetchSelfTokenAndPolicies') - .perform() - .catch(); + // Refetch the token and associated policies + this.get('token.fetchSelfTokenAndPolicies') + .perform() + .catch(); - this.setProperties({ - tokenIsValid: true, - tokenIsInvalid: false, - }); - }, - () => { - this.set('token.secret', undefined); - this.setProperties({ - tokenIsValid: false, - tokenIsInvalid: true, - }); - } - ); - }, - }, -}); + this.setProperties({ + tokenIsValid: true, + tokenIsInvalid: false, + }); + }, + () => { + this.set('token.secret', undefined); + this.setProperties({ + tokenIsValid: false, + tokenIsInvalid: true, + }); + } + ); + } +} diff --git a/ui/app/routes/application.js b/ui/app/routes/application.js index d97eb853c..1e7500924 100644 --- a/ui/app/routes/application.js +++ b/ui/app/routes/application.js @@ -3,24 +3,27 @@ import { next } from '@ember/runloop'; import Route from '@ember/routing/route'; import { AbortError } from '@ember-data/adapter/error'; import RSVP from 'rsvp'; +import { action } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default Route.extend({ - config: service(), - system: service(), - store: service(), - token: service(), +@classic +export default class ApplicationRoute extends Route { + @service config; + @service system; + @service store; + @service token; - queryParams: { + queryParams = { region: { refreshModel: true, }, - }, + }; resetController(controller, isExiting) { if (isExiting) { controller.set('error', null); } - }, + } beforeModel(transition) { const fetchSelfTokenAndPolicies = this.get('token.fetchSelfTokenAndPolicies') @@ -51,13 +54,13 @@ export default Route.extend({ return promises; }); - }, + } // Model is being used as a way to transfer the provided region // query param to update the controller state. model(params) { return params.region; - }, + } setupController(controller, model) { const queryParam = model; @@ -68,24 +71,25 @@ export default Route.extend({ }); } - return this._super(...arguments); - }, + return super.setupController(...arguments); + } - actions: { - didTransition() { - if (!this.get('config.isTest')) { - window.scrollTo(0, 0); - } - }, + @action + didTransition() { + if (!this.get('config.isTest')) { + window.scrollTo(0, 0); + } + } - willTransition() { - this.controllerFor('application').set('error', null); - }, + @action + willTransition() { + this.controllerFor('application').set('error', null); + } - error(error) { - if (!(error instanceof AbortError)) { - this.controllerFor('application').set('error', error); - } - }, - }, -}); + @action + error(error) { + if (!(error instanceof AbortError)) { + this.controllerFor('application').set('error', error); + } + } +} diff --git a/ui/app/routes/clients.js b/ui/app/routes/clients.js index 23c387284..404023e75 100644 --- a/ui/app/routes/clients.js +++ b/ui/app/routes/clients.js @@ -3,26 +3,28 @@ import Route from '@ember/routing/route'; import RSVP from 'rsvp'; import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state'; import notifyForbidden from 'nomad-ui/utils/notify-forbidden'; +import classic from 'ember-classic-decorator'; -export default Route.extend(WithForbiddenState, { - store: service(), - system: service(), +@classic +export default class ClientsRoute extends Route.extend(WithForbiddenState) { + @service store; + @service system; - breadcrumbs: Object.freeze([ + breadcrumbs = Object.freeze([ { label: 'Clients', args: ['clients.index'], }, - ]), + ]); beforeModel() { return this.get('system.leader'); - }, + } model() { return RSVP.hash({ nodes: this.store.findAll('node'), agents: this.store.findAll('agent'), }).catch(notifyForbidden(this)); - }, -}); + } +} diff --git a/ui/app/routes/csi/plugins.js b/ui/app/routes/csi/plugins.js index 30b181220..d6e6fefad 100644 --- a/ui/app/routes/csi/plugins.js +++ b/ui/app/routes/csi/plugins.js @@ -3,17 +3,17 @@ import Route from '@ember/routing/route'; import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state'; import notifyForbidden from 'nomad-ui/utils/notify-forbidden'; -export default Route.extend(WithForbiddenState, { - store: service(), +export default class PluginsRoute extends Route.extend(WithForbiddenState) { + @service store; - breadcrumbs: Object.freeze([ + breadcrumbs = Object.freeze([ { label: 'Storage', args: ['csi.index'], }, - ]), + ]); model() { return this.store.query('plugin', { type: 'csi' }).catch(notifyForbidden(this)); - }, -}); + } +} diff --git a/ui/app/routes/csi/volumes.js b/ui/app/routes/csi/volumes.js index fe49f7639..84548d8ca 100644 --- a/ui/app/routes/csi/volumes.js +++ b/ui/app/routes/csi/volumes.js @@ -2,23 +2,25 @@ import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state'; import notifyForbidden from 'nomad-ui/utils/notify-forbidden'; +import classic from 'ember-classic-decorator'; -export default Route.extend(WithForbiddenState, { - system: service(), - store: service(), +@classic +export default class VolumesRoute extends Route.extend(WithForbiddenState) { + @service system; + @service store; - breadcrumbs: Object.freeze([ + breadcrumbs = Object.freeze([ { label: 'Storage', args: ['csi.index'], }, - ]), + ]); - queryParams: { + queryParams = { volumeNamespace: { refreshModel: true, }, - }, + }; beforeModel(transition) { return this.get('system.namespaces').then(namespaces => { @@ -27,7 +29,7 @@ export default Route.extend(WithForbiddenState, { return namespaces; }); - }, + } model() { return this.store @@ -37,5 +39,5 @@ export default Route.extend(WithForbiddenState, { return volumes; }) .catch(notifyForbidden(this)); - }, -}); + } +} diff --git a/ui/app/routes/exec.js b/ui/app/routes/exec.js index 883c0f44d..4ad7fbea9 100644 --- a/ui/app/routes/exec.js +++ b/ui/app/routes/exec.js @@ -4,14 +4,16 @@ import notifyError from 'nomad-ui/utils/notify-error'; import { collect } from '@ember/object/computed'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; import { watchRecord, watchRelationship } from 'nomad-ui/utils/properties/watch'; +import classic from 'ember-classic-decorator'; -export default Route.extend(WithWatchers, { - store: service(), - token: service(), +@classic +export default class ExecRoute extends Route.extend(WithWatchers) { + @service store; + @service token; serialize(model) { return { job_name: model.get('plainId') }; - }, + } model(params, transition) { const namespace = transition.to.queryParams.namespace || this.get('system.activeNamespace.id'); @@ -28,22 +30,22 @@ export default Route.extend(WithWatchers, { const xtermImport = import('xterm').then(module => module.Terminal); return Promise.all([jobPromise, xtermImport]); - }, + } setupController(controller, [job, Terminal]) { - this._super(controller, job); + super.setupController(controller, job); controller.setUpTerminal(Terminal); - }, + } startWatchers(controller, model) { if (model) { controller.set('watcher', this.watch.perform(model)); controller.set('watchAllocations', this.watchAllocations.perform(model)); } - }, + } - watch: watchRecord('job'), - watchAllocations: watchRelationship('allocations'), + @watchRecord('job') watch; + @watchRelationship('allocations') watchAllocations; - watchers: collect('watch', 'watchAllocations'), -}); + @collect('watch', 'watchAllocations') watchers; +} diff --git a/ui/app/routes/jobs.js b/ui/app/routes/jobs.js index 42b2e1cb5..e84f2efe7 100644 --- a/ui/app/routes/jobs.js +++ b/ui/app/routes/jobs.js @@ -2,23 +2,26 @@ import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state'; import notifyForbidden from 'nomad-ui/utils/notify-forbidden'; +import { action } from '@ember/object'; +import classic from 'ember-classic-decorator'; -export default Route.extend(WithForbiddenState, { - system: service(), - store: service(), +@classic +export default class JobsRoute extends Route.extend(WithForbiddenState) { + @service store; + @service system; - breadcrumbs: Object.freeze([ + breadcrumbs = Object.freeze([ { label: 'Jobs', args: ['jobs.index'], }, - ]), + ]); - queryParams: { + queryParams = { jobNamespace: { refreshModel: true, }, - }, + }; beforeModel(transition) { return this.get('system.namespaces').then(namespaces => { @@ -27,15 +30,14 @@ export default Route.extend(WithForbiddenState, { return namespaces; }); - }, + } model() { return this.store.findAll('job', { reload: true }).catch(notifyForbidden(this)); - }, + } - actions: { - refreshRoute() { - this.refresh(); - }, - }, -}); + @action + refreshRoute() { + this.refresh(); + } +} diff --git a/ui/app/routes/jobs/run.js b/ui/app/routes/jobs/run.js index 3f45bf006..417bbff97 100644 --- a/ui/app/routes/jobs/run.js +++ b/ui/app/routes/jobs/run.js @@ -1,33 +1,35 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; +import classic from 'ember-classic-decorator'; -export default Route.extend({ - can: service(), - store: service(), - system: service(), +@classic +export default class RunRoute extends Route { + @service can; + @service store; + @service system; - breadcrumbs: Object.freeze([ + breadcrumbs = Object.freeze([ { label: 'Run', args: ['jobs.run'], }, - ]), + ]); beforeModel() { if (this.can.cannot('run job')) { this.transitionTo('jobs'); } - }, + } model() { return this.store.createRecord('job', { namespace: this.get('system.activeNamespace'), }); - }, + } resetController(controller, isExiting) { if (isExiting) { controller.model.deleteRecord(); } - }, -}); + } +} diff --git a/ui/app/routes/servers.js b/ui/app/routes/servers.js index 62ee43b89..c48de5411 100644 --- a/ui/app/routes/servers.js +++ b/ui/app/routes/servers.js @@ -3,26 +3,28 @@ import Route from '@ember/routing/route'; import RSVP from 'rsvp'; import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state'; import notifyForbidden from 'nomad-ui/utils/notify-forbidden'; +import classic from 'ember-classic-decorator'; -export default Route.extend(WithForbiddenState, { - store: service(), - system: service(), +@classic +export default class ServersRoute extends Route.extend(WithForbiddenState) { + @service store; + @service system; - breadcrumbs: Object.freeze([ + breadcrumbs = Object.freeze([ { label: 'Servers', args: ['servers.index'], }, - ]), + ]); beforeModel() { return this.get('system.leader'); - }, + } model() { return RSVP.hash({ nodes: this.store.findAll('node'), agents: this.store.findAll('agent'), }).catch(notifyForbidden(this)); - }, -}); + } +} diff --git a/ui/app/serializers/agent.js b/ui/app/serializers/agent.js index 5cbe4c3ad..75b4ce111 100644 --- a/ui/app/serializers/agent.js +++ b/ui/app/serializers/agent.js @@ -1,12 +1,12 @@ import ApplicationSerializer from './application'; import AdapterError from '@ember-data/adapter/error'; -export default ApplicationSerializer.extend({ - attrs: { +export default class AgentSerializer extends ApplicationSerializer { + attrs = { datacenter: 'dc', address: 'Addr', serfPort: 'Port', - }, + }; normalize(typeHash, hash) { if (!hash) { @@ -24,14 +24,14 @@ export default ApplicationSerializer.extend({ hash.Region = hash.Tags && hash.Tags.region; hash.RpcPort = hash.Tags && hash.Tags.port; - return this._super(typeHash, hash); - }, + return super.normalize(typeHash, hash); + } normalizeResponse(store, typeClass, hash, ...args) { - return this._super(store, typeClass, hash.Members || [], ...args); - }, + return super.normalizeResponse(store, typeClass, hash.Members || [], ...args); + } normalizeSingleResponse(store, typeClass, hash, id, ...args) { - return this._super(store, typeClass, hash.findBy('Name', id), id, ...args); - }, -}); + return super.normalizeSingleResponse(store, typeClass, hash.findBy('Name', id), id, ...args); + } +} diff --git a/ui/app/serializers/allocation.js b/ui/app/serializers/allocation.js index 43ace830f..7095d786a 100644 --- a/ui/app/serializers/allocation.js +++ b/ui/app/serializers/allocation.js @@ -8,13 +8,13 @@ const taskGroupFromJob = (job, taskGroupName) => { return taskGroup ? taskGroup : null; }; -export default ApplicationSerializer.extend({ - system: service(), +export default class AllocationSerializer extends ApplicationSerializer { + @service system; - attrs: { + attrs = { taskGroupName: 'TaskGroup', states: 'TaskStates', - }, + }; normalize(typeHash, hash) { // Transform the map-based TaskStates object into an array-based @@ -63,6 +63,6 @@ export default ApplicationSerializer.extend({ // The Job definition for an allocation is only included in findRecord responses. hash.AllocationTaskGroup = !hash.Job ? null : taskGroupFromJob(hash.Job, hash.TaskGroup); - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/deployment.js b/ui/app/serializers/deployment.js index 456eb1b25..1c57286f3 100644 --- a/ui/app/serializers/deployment.js +++ b/ui/app/serializers/deployment.js @@ -1,11 +1,13 @@ import { get } from '@ember/object'; import { assign } from '@ember/polyfills'; import ApplicationSerializer from './application'; +import classic from 'ember-classic-decorator'; -export default ApplicationSerializer.extend({ - attrs: { +@classic +export default class DeploymentSerializer extends ApplicationSerializer { + attrs = { versionNumber: 'JobVersion', - }, + }; normalize(typeHash, hash) { if (hash) { @@ -29,8 +31,8 @@ export default ApplicationSerializer.extend({ hash.JobID = hash.JobForLatestID = JSON.stringify([hash.JobID, hash.Namespace]); } - return this._super(typeHash, hash); - }, + return super.normalize(typeHash, hash); + } extractRelationships(modelClass, hash) { const namespace = this.store.adapterFor(modelClass.modelName).get('namespace'); @@ -44,7 +46,7 @@ export default ApplicationSerializer.extend({ }, }, }, - this._super(modelClass, hash) + super.extractRelationships(modelClass, hash) ); - }, -}); + } +} diff --git a/ui/app/serializers/job-version.js b/ui/app/serializers/job-version.js index 4e250d5d8..bc2962a53 100644 --- a/ui/app/serializers/job-version.js +++ b/ui/app/serializers/job-version.js @@ -1,10 +1,10 @@ import { assign } from '@ember/polyfills'; import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - attrs: { +export default class JobVersionSerializer extends ApplicationSerializer { + attrs = { number: 'Version', - }, + }; normalizeFindHasManyResponse(store, modelClass, hash, id, requestType) { const zippedVersions = hash.Versions.map((version, index) => @@ -16,6 +16,13 @@ export default ApplicationSerializer.extend({ SubmitTimeNanos: version.SubmitTime % 1000000, }) ); - return this._super(store, modelClass, zippedVersions, hash, id, requestType); - }, -}); + return super.normalizeFindHasManyResponse( + store, + modelClass, + zippedVersions, + hash, + id, + requestType + ); + } +} diff --git a/ui/app/serializers/job.js b/ui/app/serializers/job.js index a43badba1..ded935c00 100644 --- a/ui/app/serializers/job.js +++ b/ui/app/serializers/job.js @@ -2,10 +2,10 @@ import { assign } from '@ember/polyfills'; import ApplicationSerializer from './application'; import queryString from 'query-string'; -export default ApplicationSerializer.extend({ - attrs: { +export default class JobSerializer extends ApplicationSerializer { + attrs = { parameterized: 'ParameterizedJob', - }, + }; normalize(typeHash, hash) { hash.NamespaceID = hash.Namespace; @@ -45,8 +45,8 @@ export default ApplicationSerializer.extend({ }); } - return this._super(typeHash, hash); - }, + return super.normalize(typeHash, hash); + } extractRelationships(modelClass, hash) { const namespace = @@ -58,7 +58,7 @@ export default ApplicationSerializer.extend({ .buildURL(modelName, hash.ID, hash, 'findRecord') .split('?'); - return assign(this._super(...arguments), { + return assign(super.extractRelationships(...arguments), { allocations: { links: { related: buildURL(`${jobURL}/allocations`, { namespace }), @@ -85,8 +85,8 @@ export default ApplicationSerializer.extend({ }, }, }); - }, -}); + } +} function buildURL(path, queryParams) { const qpString = queryString.stringify(queryParams); diff --git a/ui/app/serializers/network.js b/ui/app/serializers/network.js index b5767df2e..3310db18c 100644 --- a/ui/app/serializers/network.js +++ b/ui/app/serializers/network.js @@ -1,12 +1,12 @@ import ApplicationSerializer from './application'; import isIp from 'is-ip'; -export default ApplicationSerializer.extend({ - attrs: { +export default class NetworkSerializer extends ApplicationSerializer { + attrs = { cidr: 'CIDR', ip: 'IP', mbits: 'MBits', - }, + }; normalize(typeHash, hash) { const ip = hash.IP; @@ -31,6 +31,6 @@ export default ApplicationSerializer.extend({ hash.Ports = reservedPorts.concat(dynamicPorts).sortBy('name'); - return this._super(...arguments); - }, -}); + return super.normalize(...arguments); + } +} diff --git a/ui/app/serializers/node-event.js b/ui/app/serializers/node-event.js index 606c6dadd..169b65faf 100644 --- a/ui/app/serializers/node-event.js +++ b/ui/app/serializers/node-event.js @@ -1,7 +1,7 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - attrs: { +export default class NodeEventSerializer extends ApplicationSerializer { + attrs = { time: 'Timestamp', - }, -}); + }; +} diff --git a/ui/app/serializers/node.js b/ui/app/serializers/node.js index 1084f442c..4eb05b976 100644 --- a/ui/app/serializers/node.js +++ b/ui/app/serializers/node.js @@ -2,13 +2,13 @@ import { assign } from '@ember/polyfills'; import { inject as service } from '@ember/service'; import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - config: service(), +export default class NodeSerializer extends ApplicationSerializer { + @service config; - attrs: { + attrs = { isDraining: 'Drain', httpAddr: 'HTTPAddr', - }, + }; normalize(modelClass, hash) { // Transform map-based objects into array-based fragment lists @@ -20,8 +20,8 @@ export default ApplicationSerializer.extend({ const hostVolumes = hash.HostVolumes || {}; hash.HostVolumes = Object.keys(hostVolumes).map(key => hostVolumes[key]); - return this._super(modelClass, hash); - }, + return super.normalize(modelClass, hash); + } extractRelationships(modelClass, hash) { const { modelName } = modelClass; @@ -36,5 +36,5 @@ export default ApplicationSerializer.extend({ }, }, }; - }, -}); + } +} diff --git a/ui/app/serializers/resources.js b/ui/app/serializers/resources.js index a657ed43b..43c9e23b7 100644 --- a/ui/app/serializers/resources.js +++ b/ui/app/serializers/resources.js @@ -1,10 +1,10 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - attrs: { +export default class ResourcesSerializer extends ApplicationSerializer { + attrs = { cpu: 'CPU', memory: 'MemoryMB', disk: 'DiskMB', iops: 'IOPS', - }, -}); + }; +} diff --git a/ui/app/serializers/service.js b/ui/app/serializers/service.js index da532204c..107c79f28 100644 --- a/ui/app/serializers/service.js +++ b/ui/app/serializers/service.js @@ -1,15 +1,15 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - attrs: { +export default class ServiceSerializer extends ApplicationSerializer { + attrs = { connect: 'Connect', - }, + }; normalize(typeHash, hash) { if (!hash.Tags) { hash.Tags = []; } - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/task-event.js b/ui/app/serializers/task-event.js index f9955ed45..dfa43f4b6 100644 --- a/ui/app/serializers/task-event.js +++ b/ui/app/serializers/task-event.js @@ -1,9 +1,9 @@ import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - attrs: { +export default class TaskEventSerializer extends ApplicationSerializer { + attrs = { message: 'DisplayMessage', - }, + }; normalize(typeHash, hash) { // Time is in the form of nanoseconds since epoch, but JS dates @@ -13,6 +13,6 @@ export default ApplicationSerializer.extend({ hash.TimeNanos = hash.Time % 1000000; hash.Time = Math.floor(hash.Time / 1000000); - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/token.js b/ui/app/serializers/token.js index 06a73676c..0e3beb302 100644 --- a/ui/app/serializers/token.js +++ b/ui/app/serializers/token.js @@ -1,16 +1,16 @@ import { copy } from 'ember-copy'; import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - primaryKey: 'AccessorID', +export default class TokenSerializer extends ApplicationSerializer { + primaryKey = 'AccessorID'; - attrs: { + attrs = { secret: 'SecretID', - }, + }; normalize(typeHash, hash) { hash.PolicyIDs = hash.Policies; hash.PolicyNames = copy(hash.Policies); - return this._super(typeHash, hash); - }, -}); + return super.normalize(typeHash, hash); + } +} diff --git a/ui/app/serializers/volume.js b/ui/app/serializers/volume.js index 78e5a49b7..f570eb19d 100644 --- a/ui/app/serializers/volume.js +++ b/ui/app/serializers/volume.js @@ -1,12 +1,12 @@ import { set, get } from '@ember/object'; import ApplicationSerializer from './application'; -export default ApplicationSerializer.extend({ - attrs: { +export default class VolumeSerializer extends ApplicationSerializer { + attrs = { externalId: 'ExternalID', - }, + }; - embeddedRelationships: Object.freeze(['writeAllocations', 'readAllocations']), + embeddedRelationships = Object.freeze(['writeAllocations', 'readAllocations']); // Volumes treat Allocations as embedded records. Ember has an // EmbeddedRecords mixin, but it assumes an application is using @@ -35,15 +35,15 @@ export default ApplicationSerializer.extend({ hash.ReadAllocations = Object.keys(readAllocs).map(bindIDToAlloc(readAllocs)); hash.WriteAllocations = Object.keys(writeAllocs).map(bindIDToAlloc(writeAllocs)); - const normalizedHash = this._super(typeHash, hash); + const normalizedHash = super.normalize(typeHash, hash); return this.extractEmbeddedRecords(this, this.store, typeHash, normalizedHash); - }, + } keyForRelationship(attr, relationshipType) { //Embedded relationship attributes don't end in IDs if (this.embeddedRelationships.includes(attr)) return attr.capitalize(); - return this._super(attr, relationshipType); - }, + return super.keyForRelationship(attr, relationshipType); + } // Convert the embedded relationship arrays into JSONAPI included records extractEmbeddedRecords(serializer, store, typeHash, partial) { @@ -84,7 +84,7 @@ export default ApplicationSerializer.extend({ }); return partial; - }, + } normalizeEmbeddedRelationship(store, relationshipMeta, relationshipHash) { const modelName = relationshipMeta.type; @@ -92,5 +92,5 @@ export default ApplicationSerializer.extend({ const serializer = store.serializerFor(modelName); return serializer.normalize(modelClass, relationshipHash, null); - }, -}); + } +} diff --git a/ui/app/utils/classes/stream-logger.js b/ui/app/utils/classes/stream-logger.js index 98e4fb4f3..7105c09f3 100644 --- a/ui/app/utils/classes/stream-logger.js +++ b/ui/app/utils/classes/stream-logger.js @@ -4,19 +4,22 @@ import TextDecoder from 'nomad-ui/utils/classes/text-decoder'; import { decode } from 'nomad-ui/utils/stream-frames'; import AbstractLogger from './abstract-logger'; import { fetchFailure } from './log'; +import classic from 'ember-classic-decorator'; -export default EmberObject.extend(AbstractLogger, { - reader: null, +@classic +export default class StreamLogger extends EmberObject.extend(AbstractLogger) { + reader = null; - additionalParams: computed(function() { + @computed() + get additionalParams() { return { follow: true, }; - }), + } start() { return this.poll.perform(); - }, + } stop() { const reader = this.reader; @@ -24,9 +27,9 @@ export default EmberObject.extend(AbstractLogger, { reader.cancel(); } return this.poll.cancelAll(); - }, + } - poll: task(function*() { + @task(function*() { const url = this.fullUrl; const logFetch = this.logFetch; @@ -81,8 +84,11 @@ export default EmberObject.extend(AbstractLogger, { } }); } - }), -}).reopenClass({ + }) + poll; +} + +StreamLogger.reopenClass({ isSupported: !!window.ReadableStream && !isSafari(), }); diff --git a/ui/app/utils/no-leader-error.js b/ui/app/utils/no-leader-error.js index 7b70918db..c531479e5 100644 --- a/ui/app/utils/no-leader-error.js +++ b/ui/app/utils/no-leader-error.js @@ -2,6 +2,6 @@ import AdapterError from '@ember-data/adapter/error'; export const NO_LEADER = 'No cluster leader'; -export default AdapterError.extend({ - message: NO_LEADER, -}); +export default class NoLeaderError extends AdapterError { + message = NO_LEADER; +} From 6adcb17fe2c173aeb85ef3ebcd94449d683d1a8a Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Thu, 11 Jun 2020 16:26:06 -0500 Subject: [PATCH 08/14] Remove problem field It appears this gets turned into a getter-only computed property somehow, which causes problems when subclasses override it. --- ui/app/components/distribution-bar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/components/distribution-bar.js b/ui/app/components/distribution-bar.js index 271fa6806..3a7021266 100644 --- a/ui/app/components/distribution-bar.js +++ b/ui/app/components/distribution-bar.js @@ -20,7 +20,7 @@ const sumAggregate = (total, val) => total + val; @classNameBindings('isNarrow:is-narrow') export default class DistributionBar extends Component.extend(WindowResizable) { chart = null; - data = null; + // data = null; FIXME causing a getter-only exception activeDatum = null; isNarrow = false; From e7428d1876ee7d529dd033410f54e90fe006883b Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 12 Jun 2020 08:22:46 -0500 Subject: [PATCH 09/14] Rename clashing action --- ui/app/controllers/clients/client.js | 2 +- ui/app/templates/clients/client.hbs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/controllers/clients/client.js b/ui/app/controllers/clients/client.js index e1858f089..9b0121e08 100644 --- a/ui/app/controllers/clients/client.js +++ b/ui/app/controllers/clients/client.js @@ -143,7 +143,7 @@ export default class ClientController extends Controller.extend(Sortable, Search } @action - drainError(err) { + setDrainError(err) { const error = messageFromAdapterError(err) || 'Could not run drain'; this.set('drainError', error); } diff --git a/ui/app/templates/clients/client.hbs b/ui/app/templates/clients/client.hbs index 5b2e7089d..470d66e2b 100644 --- a/ui/app/templates/clients/client.hbs +++ b/ui/app/templates/clients/client.hbs @@ -126,7 +126,7 @@ @client={{model}} @isDisabled={{cannot "write client"}} @onDrain={{action "drainNotify"}} - @onError={{action "drainError"}} /> + @onError={{action "setDrainError"}} /> From 50513ea105df808af05622a8524ac9da88689957 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 12 Jun 2020 09:23:51 -0500 Subject: [PATCH 10/14] Convert field to overridable computed property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit StatsTimeSeries defines description as a computed property, which isn’t possible when this is a class field. --- ui/app/components/line-chart.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/app/components/line-chart.js b/ui/app/components/line-chart.js index 2dd5e6cee..5b47a0049 100644 --- a/ui/app/components/line-chart.js +++ b/ui/app/components/line-chart.js @@ -43,7 +43,11 @@ export default class LineChart extends Component.extend(WindowResizable) { chartClass = 'is-primary'; title = 'Line Chart'; - description = null; + + @overridable(function() { + return null; + }) + description; // Private Properties From 190400512081dfde0b44b3bc45bb180ba1412ea1 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 12 Jun 2020 09:43:54 -0500 Subject: [PATCH 11/14] Rename clashing property --- ui/app/templates/components/exec/task-contents.hbs | 2 +- ui/app/templates/components/exec/task-group-parent.hbs | 6 +++--- ui/app/templates/exec.hbs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/app/templates/components/exec/task-contents.hbs b/ui/app/templates/components/exec/task-contents.hbs index e36bea0fc..615723f3d 100644 --- a/ui/app/templates/components/exec/task-contents.hbs +++ b/ui/app/templates/components/exec/task-contents.hbs @@ -7,7 +7,7 @@ {{/if}} -{{#if openInNewWindow}} +{{#if shouldOpenInNewWindow}} {{x-icon "exit" class="show-on-hover"}} diff --git a/ui/app/templates/components/exec/task-group-parent.hbs b/ui/app/templates/components/exec/task-group-parent.hbs index 2109ca14e..72ea41808 100644 --- a/ui/app/templates/components/exec/task-group-parent.hbs +++ b/ui/app/templates/components/exec/task-group-parent.hbs @@ -5,19 +5,19 @@ {{#if isOpen}}
    {{#each sortedTasks as |task|}} - {{#if openInNewWindow}} + {{#if shouldOpenInNewWindow}} + @shouldOpenInNewWindow={{shouldOpenInNewWindow}} /> {{else}} + @shouldOpenInNewWindow={{shouldOpenInNewWindow}} /> {{/if}} {{/each}} diff --git a/ui/app/templates/exec.hbs b/ui/app/templates/exec.hbs index 385bc7025..8d853b487 100644 --- a/ui/app/templates/exec.hbs +++ b/ui/app/templates/exec.hbs @@ -46,7 +46,7 @@
  • From 36b58a95ab26a98f0ebcf3f86b4c6cb7869bfcd3 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 12 Jun 2020 15:48:52 -0500 Subject: [PATCH 12/14] Remove superfluous uses of Object.freeze This is no longer needed! https://guides.emberjs.com/release/upgrading/current-edition/native-classes/#toc_properties-and-fields --- ui/app/adapters/job.js | 4 ++-- ui/app/adapters/plugin.js | 4 ++-- ui/app/adapters/volume.js | 4 ++-- ui/app/components/exec/task-group-parent.js | 2 +- ui/app/components/page-size-select.js | 2 +- ui/app/controllers/exec.js | 2 +- ui/app/routes/clients.js | 4 ++-- ui/app/routes/csi/plugins.js | 4 ++-- ui/app/routes/csi/volumes.js | 4 ++-- ui/app/routes/jobs.js | 4 ++-- ui/app/routes/jobs/run.js | 4 ++-- ui/app/routes/servers.js | 4 ++-- ui/app/serializers/volume.js | 2 +- 13 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index a052e5c8d..bb18b2719 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -2,9 +2,9 @@ import WatchableNamespaceIDs from './watchable-namespace-ids'; import addToPath from 'nomad-ui/utils/add-to-path'; export default class JobAdapter extends WatchableNamespaceIDs { - relationshipFallbackLinks = Object.freeze({ + relationshipFallbackLinks = { summary: '/summary', - }); + }; fetchRawDefinition(job) { const url = this.urlForFindRecord(job.get('id'), 'job'); diff --git a/ui/app/adapters/plugin.js b/ui/app/adapters/plugin.js index ff304dbdf..80096f31b 100644 --- a/ui/app/adapters/plugin.js +++ b/ui/app/adapters/plugin.js @@ -1,7 +1,7 @@ import Watchable from './watchable'; export default class PluginAdapter extends Watchable { - queryParamsToAttrs = Object.freeze({ + queryParamsToAttrs = { type: 'type', - }); + }; } diff --git a/ui/app/adapters/volume.js b/ui/app/adapters/volume.js index fbc665b32..594f929a1 100644 --- a/ui/app/adapters/volume.js +++ b/ui/app/adapters/volume.js @@ -1,8 +1,8 @@ import WatchableNamespaceIDs from './watchable-namespace-ids'; export default class VolumeAdapter extends WatchableNamespaceIDs { - queryParamsToAttrs = Object.freeze({ + queryParamsToAttrs = { type: 'type', plugin_id: 'plugin.id', - }); + }; } diff --git a/ui/app/components/exec/task-group-parent.js b/ui/app/components/exec/task-group-parent.js index f56b7ecb1..8537d9f01 100644 --- a/ui/app/components/exec/task-group-parent.js +++ b/ui/app/components/exec/task-group-parent.js @@ -63,7 +63,7 @@ export default class TaskGroupParent extends Component { return this.taskGroup.tasks.filter(task => activeTaskStateNames.includes(task.name)); } - taskSorting = Object.freeze(['name']); + taskSorting = ['name']; @sort('tasksWithRunningStates', 'taskSorting') sortedTasks; clickedOpen = false; diff --git a/ui/app/components/page-size-select.js b/ui/app/components/page-size-select.js index 3c6c3f0b3..2151c4c58 100644 --- a/ui/app/components/page-size-select.js +++ b/ui/app/components/page-size-select.js @@ -5,7 +5,7 @@ export default class PageSizeSelect extends Component { @service userSettings; tagName = ''; - pageSizeOptions = Object.freeze([10, 25, 50]); + pageSizeOptions = [10, 25, 50]; onChange() {} } diff --git a/ui/app/controllers/exec.js b/ui/app/controllers/exec.js index d648753fc..9e2691527 100644 --- a/ui/app/controllers/exec.js +++ b/ui/app/controllers/exec.js @@ -32,7 +32,7 @@ export default class ExecController extends Controller { @mapBy('pendingAndRunningAllocations', 'taskGroup') pendingAndRunningTaskGroups; @uniq('pendingAndRunningTaskGroups') uniquePendingAndRunningTaskGroups; - taskGroupSorting = Object.freeze(['name']); + taskGroupSorting = ['name']; @sort('uniquePendingAndRunningTaskGroups', 'taskGroupSorting') sortedTaskGroups; setUpTerminal(Terminal) { diff --git a/ui/app/routes/clients.js b/ui/app/routes/clients.js index 404023e75..c0c85460e 100644 --- a/ui/app/routes/clients.js +++ b/ui/app/routes/clients.js @@ -10,12 +10,12 @@ export default class ClientsRoute extends Route.extend(WithForbiddenState) { @service store; @service system; - breadcrumbs = Object.freeze([ + breadcrumbs = [ { label: 'Clients', args: ['clients.index'], }, - ]); + ]; beforeModel() { return this.get('system.leader'); diff --git a/ui/app/routes/csi/plugins.js b/ui/app/routes/csi/plugins.js index d6e6fefad..0ddd99f36 100644 --- a/ui/app/routes/csi/plugins.js +++ b/ui/app/routes/csi/plugins.js @@ -6,12 +6,12 @@ import notifyForbidden from 'nomad-ui/utils/notify-forbidden'; export default class PluginsRoute extends Route.extend(WithForbiddenState) { @service store; - breadcrumbs = Object.freeze([ + breadcrumbs = [ { label: 'Storage', args: ['csi.index'], }, - ]); + ]; model() { return this.store.query('plugin', { type: 'csi' }).catch(notifyForbidden(this)); diff --git a/ui/app/routes/csi/volumes.js b/ui/app/routes/csi/volumes.js index 84548d8ca..14d0ada01 100644 --- a/ui/app/routes/csi/volumes.js +++ b/ui/app/routes/csi/volumes.js @@ -9,12 +9,12 @@ export default class VolumesRoute extends Route.extend(WithForbiddenState) { @service system; @service store; - breadcrumbs = Object.freeze([ + breadcrumbs = [ { label: 'Storage', args: ['csi.index'], }, - ]); + ]; queryParams = { volumeNamespace: { diff --git a/ui/app/routes/jobs.js b/ui/app/routes/jobs.js index e84f2efe7..8ba64a9e2 100644 --- a/ui/app/routes/jobs.js +++ b/ui/app/routes/jobs.js @@ -10,12 +10,12 @@ export default class JobsRoute extends Route.extend(WithForbiddenState) { @service store; @service system; - breadcrumbs = Object.freeze([ + breadcrumbs = [ { label: 'Jobs', args: ['jobs.index'], }, - ]); + ]; queryParams = { jobNamespace: { diff --git a/ui/app/routes/jobs/run.js b/ui/app/routes/jobs/run.js index 417bbff97..c7a9de74f 100644 --- a/ui/app/routes/jobs/run.js +++ b/ui/app/routes/jobs/run.js @@ -8,12 +8,12 @@ export default class RunRoute extends Route { @service store; @service system; - breadcrumbs = Object.freeze([ + breadcrumbs = [ { label: 'Run', args: ['jobs.run'], }, - ]); + ]; beforeModel() { if (this.can.cannot('run job')) { diff --git a/ui/app/routes/servers.js b/ui/app/routes/servers.js index c48de5411..5976dfaff 100644 --- a/ui/app/routes/servers.js +++ b/ui/app/routes/servers.js @@ -10,12 +10,12 @@ export default class ServersRoute extends Route.extend(WithForbiddenState) { @service store; @service system; - breadcrumbs = Object.freeze([ + breadcrumbs = [ { label: 'Servers', args: ['servers.index'], }, - ]); + ]; beforeModel() { return this.get('system.leader'); diff --git a/ui/app/serializers/volume.js b/ui/app/serializers/volume.js index f570eb19d..2b6e055f1 100644 --- a/ui/app/serializers/volume.js +++ b/ui/app/serializers/volume.js @@ -6,7 +6,7 @@ export default class VolumeSerializer extends ApplicationSerializer { externalId: 'ExternalID', }; - embeddedRelationships = Object.freeze(['writeAllocations', 'readAllocations']); + embeddedRelationships = ['writeAllocations', 'readAllocations']; // Volumes treat Allocations as embedded records. Ember has an // EmbeddedRecords mixin, but it assumes an application is using From 7fabdd56e662a7bfe452b30895c49fd04bcdba20 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Fri, 12 Jun 2020 15:58:21 -0700 Subject: [PATCH 13/14] Data cannot be a field in the base class and a CP in the child classes --- ui/app/components/distribution-bar.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/app/components/distribution-bar.js b/ui/app/components/distribution-bar.js index 3a7021266..ee9af8456 100644 --- a/ui/app/components/distribution-bar.js +++ b/ui/app/components/distribution-bar.js @@ -6,6 +6,7 @@ import { run } from '@ember/runloop'; import { assign } from '@ember/polyfills'; import { guidFor } from '@ember/object/internals'; import { copy } from 'ember-copy'; +import { computed as overridable } from 'ember-overridable-computed'; import d3 from 'd3-selection'; import 'd3-transition'; import WindowResizable from '../mixins/window-resizable'; @@ -20,7 +21,7 @@ const sumAggregate = (total, val) => total + val; @classNameBindings('isNarrow:is-narrow') export default class DistributionBar extends Component.extend(WindowResizable) { chart = null; - // data = null; FIXME causing a getter-only exception + @overridable(() => null) data; activeDatum = null; isNarrow = false; From 0cf59f10be7cdcd5f7c2f339c56263b9997c2245 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Tue, 16 Jun 2020 08:33:40 -0500 Subject: [PATCH 14/14] Remove stray commented-out line --- ui/app/adapters/job.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index bb18b2719..f6992cb13 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -62,7 +62,6 @@ export default class JobAdapter extends WatchableNamespaceIDs { update(job) { const jobId = job.get('id') || job.get('_idBeforeSaving'); - // debugger; return this.ajax(this.urlForUpdateRecord(jobId, 'job'), 'POST', { data: { Job: job.get('_newDefinitionJSON'),