From 11d80ae489b3f8bea5879407456da614fe0d170b Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Tue, 9 Jun 2020 16:03:28 -0500 Subject: [PATCH] Add Ember ESLint plugin (#8134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is extracted from #8094, where I have run into some snags. Since these ESLint fixes aren’t actually connected to the Ember 3.16 update but involve changes to many files, we might as well address them separately. Where possible I fixed the problems but in cases where a fix seemed too involved, I added per-line or -file exceptions. --- .circleci/config.yml | 6 +-- .circleci/config/jobs/test-ui.yml | 6 +-- ui/.eslintrc.js | 8 ++- ui/app/adapters/application.js | 2 + ui/app/adapters/job.js | 4 +- ui/app/adapters/plugin.js | 4 +- ui/app/adapters/volume.js | 4 +- ui/app/components/allocation-stat.js | 16 +++--- ui/app/components/distribution-bar.js | 1 + ui/app/components/drain-popover.js | 18 ++++--- ui/app/components/fs/browser.js | 3 +- ui/app/components/fs/file.js | 18 +++---- ui/app/components/lifecycle-chart-row.js | 4 ++ ui/app/components/line-chart.js | 1 + ui/app/components/list-accordion.js | 1 + ui/app/components/multi-select-dropdown.js | 8 +-- ui/app/components/popover-menu.js | 10 ++-- ui/app/components/streaming-file.js | 53 ++++++++++--------- ui/app/components/task-log.js | 2 +- ui/app/components/task-row.js | 4 +- .../allocations/allocation/index.js | 1 + .../allocations/allocation/task/index.js | 2 +- ui/app/controllers/application.js | 1 + ui/app/controllers/clients/client.js | 5 +- ui/app/controllers/clients/index.js | 24 ++++++--- ui/app/controllers/csi/plugins/index.js | 8 ++- .../csi/plugins/plugin/allocations.js | 19 +++---- ui/app/controllers/csi/volumes/index.js | 10 ++-- ui/app/controllers/exec.js | 8 +-- ui/app/controllers/jobs/index.js | 41 ++++++++------ ui/app/controllers/jobs/job/allocations.js | 4 +- ui/app/controllers/jobs/job/task-group.js | 4 +- ui/app/mixins/searchable.js | 15 +++--- ui/app/mixins/sortable-factory.js | 6 ++- ui/app/mixins/window-resizable.js | 11 ++-- .../with-component-visibility-detection.js | 1 + ui/app/mixins/with-forbidden-state.js | 1 + ui/app/mixins/with-model-error-handling.js | 1 + ui/app/mixins/with-namespace-ids.js | 1 + ui/app/mixins/with-namespace-resetting.js | 1 + .../mixins/with-route-visibility-detection.js | 1 + ui/app/mixins/with-watchers.js | 5 +- ui/app/models/job.js | 38 ++++++------- ui/app/models/node.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 +- ui/app/services/breadcrumbs.js | 2 +- ui/app/services/stats-trackers-registry.js | 4 +- ui/app/services/system.js | 2 + ui/app/services/token.js | 1 + ui/app/services/watch-list.js | 4 +- ui/app/utils/classes/abstract-logger.js | 1 + .../utils/classes/abstract-stats-tracker.js | 1 + ui/app/utils/classes/stream-logger.js | 8 +-- ui/app/utils/fetch.js | 3 +- ui/package.json | 1 + ui/stories/charts/distribution-bar.stories.js | 2 +- ui/stories/charts/line-chart.stories.js | 4 +- .../charts/stats-time-series.stories.js | 4 +- ui/stories/components/table.stories.js | 4 +- ui/tests/integration/app-breadcrumbs-test.js | 5 +- ui/tests/integration/primary-metric-test.js | 8 ++- ui/tests/unit/abilities/allocation-test.js | 1 + ui/tests/unit/abilities/client-test.js | 1 + ui/tests/unit/abilities/job-test.js | 1 + ui/tests/unit/mixins/searchable-test.js | 10 +++- ui/tests/unit/utils/log-test.js | 8 +-- ui/yarn.lock | 26 +++++++++ 73 files changed, 315 insertions(+), 195 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4ced53105..34574af94 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -708,13 +708,13 @@ jobs: - checkout - restore_cache: keys: - - v1-deps-{{ checksum "ui/yarn.lock" }} - - v1-deps- + - v2-deps-{{ checksum "ui/yarn.lock" }} + - v2-deps- - run: command: cd ui && yarn install name: yarn install - save_cache: - key: v1-deps-{{ checksum "ui/yarn.lock" }} + key: v2-deps-{{ checksum "ui/yarn.lock" }} paths: - ./ui/node_modules - run: diff --git a/.circleci/config/jobs/test-ui.yml b/.circleci/config/jobs/test-ui.yml index 384b4f175..866465fdf 100644 --- a/.circleci/config/jobs/test-ui.yml +++ b/.circleci/config/jobs/test-ui.yml @@ -7,13 +7,13 @@ steps: - checkout - restore_cache: keys: - - v1-deps-{{ checksum "ui/yarn.lock" }} - - v1-deps- + - v2-deps-{{ checksum "ui/yarn.lock" }} + - v2-deps- - run: name: yarn install command: cd ui && yarn install - save_cache: - key: v1-deps-{{ checksum "ui/yarn.lock" }} + key: v2-deps-{{ checksum "ui/yarn.lock" }} paths: - ./ui/node_modules - run: diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js index 2e69db5bc..51f829062 100644 --- a/ui/.eslintrc.js +++ b/ui/.eslintrc.js @@ -7,12 +7,18 @@ module.exports = { browser: true, es6: true, }, - extends: 'eslint:recommended', + extends: [ + 'eslint:recommended', + 'plugin:ember/recommended', + ], parser: 'babel-eslint', parserOptions: { ecmaVersion: 2018, sourceType: 'module', }, + plugins: [ + 'ember' + ], rules: { indent: ['error', 2, { SwitchCase: 1 }], 'linebreak-style': ['error', 'unix'], diff --git a/ui/app/adapters/application.js b/ui/app/adapters/application.js index 60a7fdacb..7bbf51b02 100644 --- a/ui/app/adapters/application.js +++ b/ui/app/adapters/application.js @@ -24,6 +24,8 @@ export default RESTAdapter.extend({ 'X-Nomad-Token': token, }; } + + return; }), handleResponse(status, headers, payload) { diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index 93ff7a9fb..2bc323838 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -3,9 +3,9 @@ import addToPath from 'nomad-ui/utils/add-to-path'; import WithNamespaceIDs from 'nomad-ui/mixins/with-namespace-ids'; export default Watchable.extend(WithNamespaceIDs, { - relationshipFallbackLinks: { + relationshipFallbackLinks: Object.freeze({ 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 c89779543..836488ad1 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: { + queryParamsToAttrs: Object.freeze({ type: 'type', - }, + }), }); diff --git a/ui/app/adapters/volume.js b/ui/app/adapters/volume.js index 551e49126..8f2d8532a 100644 --- a/ui/app/adapters/volume.js +++ b/ui/app/adapters/volume.js @@ -2,8 +2,8 @@ import Watchable from './watchable'; import WithNamespaceIDs from 'nomad-ui/mixins/with-namespace-ids'; export default Watchable.extend(WithNamespaceIDs, { - queryParamsToAttrs: { + queryParamsToAttrs: Object.freeze({ type: 'type', plugin_id: 'plugin.id', - }, + }), }); diff --git a/ui/app/components/allocation-stat.js b/ui/app/components/allocation-stat.js index c8f16a7a4..b1d702a13 100644 --- a/ui/app/components/allocation-stat.js +++ b/ui/app/components/allocation-stat.js @@ -24,6 +24,8 @@ export default Component.extend({ if (metric === 'cpu' || metric === 'memory') { return this[this.metric]; } + + return; }), formattedStat: computed('metric', 'stat.used', function() { @@ -32,13 +34,9 @@ export default Component.extend({ return this.stat.used; }), - formattedReserved: computed( - 'metric', - 'statsTracker.reservedMemory', - 'statsTracker.reservedCPU', - function() { - if (this.metric === 'memory') return `${this.statsTracker.reservedMemory} MiB`; - if (this.metric === 'cpu') return `${this.statsTracker.reservedCPU} MHz`; - } - ), + formattedReserved: computed('metric', 'statsTracker.{reservedMemory,reservedCPU}', function() { + 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/distribution-bar.js b/ui/app/components/distribution-bar.js index ee672b4ac..743aead63 100644 --- a/ui/app/components/distribution-bar.js +++ b/ui/app/components/distribution-bar.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import Component from '@ember/component'; import { computed, observer, set } from '@ember/object'; import { run } from '@ember/runloop'; diff --git a/ui/app/components/drain-popover.js b/ui/app/components/drain-popover.js index c5f2187e4..f964a8081 100644 --- a/ui/app/components/drain-popover.js +++ b/ui/app/components/drain-popover.js @@ -27,14 +27,16 @@ export default Component.extend({ durationIsCustom: equal('selectedDurationQuickOption.value', 'custom'), customDuration: '', - durationQuickOptions: computed(() => [ - { label: '1 Hour', value: '1h' }, - { label: '4 Hours', value: '4h' }, - { label: '8 Hours', value: '8h' }, - { label: '12 Hours', value: '12h' }, - { label: '1 Day', value: '1d' }, - { label: 'Custom', value: 'custom' }, - ]), + durationQuickOptions: computed(function() { + return [ + { label: '1 Hour', value: '1h' }, + { label: '4 Hours', value: '4h' }, + { label: '8 Hours', value: '8h' }, + { label: '12 Hours', value: '12h' }, + { label: '1 Day', value: '1d' }, + { label: 'Custom', value: 'custom' }, + ]; + }), deadline: computed( 'deadlineEnabled', diff --git a/ui/app/components/fs/browser.js b/ui/app/components/fs/browser.js index 9a7e321e2..bf6b30731 100644 --- a/ui/app/components/fs/browser.js +++ b/ui/app/components/fs/browser.js @@ -19,6 +19,8 @@ export default Component.extend({ if (this.model.allocation) { return this.model; } + + return; }), type: computed('taskState', function() { @@ -53,5 +55,4 @@ export default Component.extend({ } } ), - }); diff --git a/ui/app/components/fs/file.js b/ui/app/components/fs/file.js index 36739853c..c8e6a06ec 100644 --- a/ui/app/components/fs/file.js +++ b/ui/app/components/fs/file.js @@ -64,19 +64,15 @@ export default Component.extend({ } else if (this.mode === 'head' || this.mode === 'tail') { return 'readat'; } + + return; }), - fileUrl: computed( - 'allocation.id', - 'allocation.node.httpAddr', - 'fetchMode', - 'useServer', - function() { - const address = this.get('allocation.node.httpAddr'); - const url = `/v1/client/fs/${this.fetchMode}/${this.allocation.id}`; - return this.useServer ? url : `//${address}${url}`; - } - ), + fileUrl: computed('allocation.{id,node.httpAddr}', 'fetchMode', 'useServer', function() { + 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() { // The Log class handles encoding query params diff --git a/ui/app/components/lifecycle-chart-row.js b/ui/app/components/lifecycle-chart-row.js index 5a3a5e9dd..8ef057556 100644 --- a/ui/app/components/lifecycle-chart-row.js +++ b/ui/app/components/lifecycle-chart-row.js @@ -8,11 +8,15 @@ export default Component.extend({ if (this.taskState && this.taskState.state === 'running') { return 'is-active'; } + + return; }), finishedClass: computed('taskState.finishedAt', function() { if (this.taskState && this.taskState.finishedAt) { return 'is-finished'; } + + return; }), }); diff --git a/ui/app/components/line-chart.js b/ui/app/components/line-chart.js index a5c1d9258..d0b282db6 100644 --- a/ui/app/components/line-chart.js +++ b/ui/app/components/line-chart.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import Component from '@ember/component'; import { computed, observer } from '@ember/object'; import { computed as overridable } from 'ember-overridable-computed'; diff --git a/ui/app/components/list-accordion.js b/ui/app/components/list-accordion.js index 5f83b5ee0..105288f1b 100644 --- a/ui/app/components/list-accordion.js +++ b/ui/app/components/list-accordion.js @@ -25,6 +25,7 @@ export default Component.extend({ }; }); + // eslint-disable-next-line ember/no-side-effects this.set('stateCache', decoratedSource); return decoratedSource; }), diff --git a/ui/app/components/multi-select-dropdown.js b/ui/app/components/multi-select-dropdown.js index 3bd9febcc..53e438d95 100644 --- a/ui/app/components/multi-select-dropdown.js +++ b/ui/app/components/multi-select-dropdown.js @@ -29,12 +29,14 @@ export default Component.extend({ didReceiveAttrs() { const dropdown = this.dropdown; if (this.isOpen && dropdown) { - run.scheduleOnce('afterRender', () => { - dropdown.actions.reposition(); - }); + run.scheduleOnce('afterRender', this, this.repositionDropdown); } }, + repositionDropdown() { + this.dropdown.actions.reposition(); + }, + actions: { toggle({ key }) { const newSelection = this.selection.slice(); diff --git a/ui/app/components/popover-menu.js b/ui/app/components/popover-menu.js index 92824e5ef..20e5fc638 100644 --- a/ui/app/components/popover-menu.js +++ b/ui/app/components/popover-menu.js @@ -12,7 +12,7 @@ const FOCUSABLE = [ ].join(', '); export default Component.extend({ - classnames: ['popover'], + classNames: ['popover'], triggerClass: '', isOpen: false, @@ -31,12 +31,14 @@ export default Component.extend({ didReceiveAttrs() { const dropdown = this.dropdown; if (this.isOpen && dropdown) { - run.scheduleOnce('afterRender', () => { - dropdown.actions.reposition(); - }); + run.scheduleOnce('afterRender', this, this.repositionDropdown); } }, + repositionDropdown() { + this.dropdown.actions.reposition(); + }, + actions: { openOnArrowDown(dropdown, e) { if (!this.isOpen && e.keyCode === ARROW_DOWN) { diff --git a/ui/app/components/streaming-file.js b/ui/app/components/streaming-file.js index 5885c2bc8..b35963574 100644 --- a/ui/app/components/streaming-file.js +++ b/ui/app/components/streaming-file.js @@ -17,23 +17,25 @@ export default Component.extend(WindowResizable, { return; } - run.scheduleOnce('actions', () => { - switch (this.mode) { - case 'head': - this.head.perform(); - break; - case 'tail': - this.tail.perform(); - break; - case 'streaming': - if (this.isStreaming) { - this.stream.perform(); - } else { - this.logger.stop(); - } - break; - } - }); + run.scheduleOnce('actions', this, this.performTask); + }, + + performTask() { + switch (this.mode) { + case 'head': + this.head.perform(); + break; + case 'tail': + this.tail.perform(); + break; + case 'streaming': + if (this.isStreaming) { + this.stream.perform(); + } else { + this.logger.stop(); + } + break; + } }, didInsertElement() { @@ -54,17 +56,16 @@ export default Component.extend(WindowResizable, { head: task(function*() { yield this.get('logger.gotoHead').perform(); - run.scheduleOnce('afterRender', () => { - this.element.scrollTop = 0; - }); + run.scheduleOnce('afterRender', this, this.scrollToTop); }), + scrollToTop() { + this.element.scrollTop = 0; + }, + tail: task(function*() { yield this.get('logger.gotoTail').perform(); - run.scheduleOnce('afterRender', () => { - const cliWindow = this.element; - cliWindow.scrollTop = cliWindow.scrollHeight; - }); + run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition, [true]); }), synchronizeScrollPosition(force = false) { @@ -78,7 +79,7 @@ export default Component.extend(WindowResizable, { stream: task(function*() { // Force the scroll position to the bottom of the window when starting streaming this.logger.one('tick', () => { - run.scheduleOnce('afterRender', () => this.synchronizeScrollPosition(true)); + run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition, [true]); }); // Follow the log if the scroll position is near the bottom of the cli window @@ -89,7 +90,7 @@ export default Component.extend(WindowResizable, { }), scheduleScrollSynchronization() { - run.scheduleOnce('afterRender', () => this.synchronizeScrollPosition()); + run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition); }, willDestroy() { diff --git a/ui/app/components/task-log.js b/ui/app/components/task-log.js index 108a9b300..deaef2c89 100644 --- a/ui/app/components/task-log.js +++ b/ui/app/components/task-log.js @@ -28,7 +28,7 @@ export default Component.extend({ mode: 'stdout', - logUrl: computed('allocation.id', 'allocation.node.httpAddr', 'useServer', function() { + logUrl: computed('allocation.{id,node.httpAddr}', 'useServer', function() { const address = this.get('allocation.node.httpAddr'); const allocation = this.get('allocation.id'); diff --git a/ui/app/components/task-row.js b/ui/app/components/task-row.js index 393a0fd93..4901d1742 100644 --- a/ui/app/components/task-row.js +++ b/ui/app/components/task-row.js @@ -19,7 +19,9 @@ export default Component.extend({ // Internal state statsError: false, - enablePolling: computed(() => !Ember.testing), + enablePolling: computed(function() { + return !Ember.testing; + }), // Since all tasks for an allocation share the same tracker, use the registry stats: computed('task', 'task.isRunning', function() { diff --git a/ui/app/controllers/allocations/allocation/index.js b/ui/app/controllers/allocations/allocation/index.js index caec2d8ae..ebccdfa75 100644 --- a/ui/app/controllers/allocations/allocation/index.js +++ b/ui/app/controllers/allocations/allocation/index.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; import { computed, observer } from '@ember/object'; diff --git a/ui/app/controllers/allocations/allocation/task/index.js b/ui/app/controllers/allocations/allocation/task/index.js index 8c26f7854..84a62c675 100644 --- a/ui/app/controllers/allocations/allocation/task/index.js +++ b/ui/app/controllers/allocations/allocation/task/index.js @@ -15,7 +15,7 @@ export default Controller.extend({ }), network: alias('model.resources.networks.firstObject'), - ports: computed('network.reservedPorts.[]', 'network.dynamicPorts.[]', function() { + ports: computed('network.{reservedPorts.[],dynamicPorts.[]}', function() { return (this.get('network.reservedPorts') || []) .map(port => ({ name: port.Label, diff --git a/ui/app/controllers/application.js b/ui/app/controllers/application.js index 7a2934fab..0a1597503 100644 --- a/ui/app/controllers/application.js +++ b/ui/app/controllers/application.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; import { run } from '@ember/runloop'; diff --git a/ui/app/controllers/clients/client.js b/ui/app/controllers/clients/client.js index f7ca0daca..260fdc4fa 100644 --- a/ui/app/controllers/clients/client.js +++ b/ui/app/controllers/clients/client.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import { alias } from '@ember/object/computed'; import Controller from '@ember/controller'; import { computed, observer } from '@ember/object'; @@ -24,7 +25,9 @@ export default Controller.extend(Sortable, Searchable, { sortProperty: 'modifyIndex', sortDescending: true, - searchProps: computed(() => ['shortId', 'name']), + searchProps: computed(function() { + return ['shortId', 'name']; + }), onlyPreemptions: false, diff --git a/ui/app/controllers/clients/index.js b/ui/app/controllers/clients/index.js index 2c1463c41..78de1aa35 100644 --- a/ui/app/controllers/clients/index.js +++ b/ui/app/controllers/clients/index.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */ import { alias, readOnly } from '@ember/object/computed'; import { inject as service } from '@ember/service'; import Controller, { inject as controller } from '@ember/controller'; @@ -35,7 +36,9 @@ export default Controller.extend( sortProperty: 'modifyIndex', sortDescending: true, - searchProps: computed(() => ['id', 'name', 'datacenter']), + searchProps: computed(function() { + return ['id', 'name', 'datacenter']; + }), qpClass: '', qpState: '', @@ -54,25 +57,29 @@ export default Controller.extend( // 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(() => [ - { key: 'initializing', label: 'Initializing' }, - { key: 'ready', label: 'Ready' }, - { key: 'down', label: 'Down' }, - { key: 'ineligible', label: 'Ineligible' }, - { key: 'draining', label: 'Draining' }, - ]), + 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))); }); @@ -86,6 +93,7 @@ export default Controller.extend( 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))); }); diff --git a/ui/app/controllers/csi/plugins/index.js b/ui/app/controllers/csi/plugins/index.js index ba62315b9..66a888416 100644 --- a/ui/app/controllers/csi/plugins/index.js +++ b/ui/app/controllers/csi/plugins/index.js @@ -30,8 +30,12 @@ export default Controller.extend( currentPage: 1, pageSize: readOnly('userSettings.pageSize'), - searchProps: computed(() => ['id']), - fuzzySearchProps: computed(() => ['id']), + searchProps: computed(function() { + return ['id']; + }), + fuzzySearchProps: computed(function() { + return ['id']; + }), sortProperty: 'id', sortDescending: false, diff --git a/ui/app/controllers/csi/plugins/plugin/allocations.js b/ui/app/controllers/csi/plugins/plugin/allocations.js index 948599c97..941235693 100644 --- a/ui/app/controllers/csi/plugins/plugin/allocations.js +++ b/ui/app/controllers/csi/plugins/plugin/allocations.js @@ -29,24 +29,21 @@ export default Controller.extend(SortableFactory(['updateTime', 'healthy']), { selectionType: selection('qpType'), selectionHealth: selection('qpHealth'), - optionsType: computed(() => [ - { key: 'controller', label: 'Controller' }, - { key: 'node', label: 'Node' }, - ]), + optionsType: computed(function() { + return [{ key: 'controller', label: 'Controller' }, { key: 'node', label: 'Node' }]; + }), - optionsHealth: computed(() => [ - { key: 'true', label: 'Healthy' }, - { key: 'false', label: 'Unhealthy' }, - ]), + optionsHealth: computed(function() { + return [{ key: 'true', label: 'Healthy' }, { key: 'false', label: 'Unhealthy' }]; + }), - combinedAllocations: computed('model.controllers.[]', 'model.nodes.[]', function() { + combinedAllocations: computed('model.{controllers.[],nodes.[]}', function() { return this.model.controllers.toArray().concat(this.model.nodes.toArray()); }), filteredAllocations: computed( 'combinedAllocations.[]', - 'model.controllers.[]', - 'model.nodes.[]', + 'model.{controllers.[],nodes.[]}', 'selectionType', 'selectionHealth', function() { diff --git a/ui/app/controllers/csi/volumes/index.js b/ui/app/controllers/csi/volumes/index.js index 20aad2406..37a17234a 100644 --- a/ui/app/controllers/csi/volumes/index.js +++ b/ui/app/controllers/csi/volumes/index.js @@ -35,14 +35,18 @@ export default Controller.extend( sortProperty: 'id', sortDescending: false, - searchProps: computed(() => ['name']), - fuzzySearchProps: computed(() => ['name']), + searchProps: computed(function() { + return ['name']; + }), + fuzzySearchProps: computed(function() { + return ['name']; + }), fuzzySearchEnabled: true, /** Visible volumes are those that match the selected namespace */ - visibleVolumes: computed('model.[]', 'model.@each.parent', function() { + visibleVolumes: computed('model.{[],@each.parent}', function() { if (!this.model) return []; // Namespace related properties are ommitted from the dependent keys diff --git a/ui/app/controllers/exec.js b/ui/app/controllers/exec.js index a30144772..d6ea565fe 100644 --- a/ui/app/controllers/exec.js +++ b/ui/app/controllers/exec.js @@ -43,14 +43,12 @@ export default Controller.extend({ allocations: alias('model.allocations'), taskState: computed( - 'allocations.[]', + 'allocations.{[],@each.isActive}', 'allocationShortId', - 'allocations.@each.isActive', 'taskName', 'taskGroupName', 'allocation', - 'allocation.states.@each.name', - 'allocation.states.@each.isRunning', + 'allocation.states.@each.{name,isRunning}', function() { if (!this.allocations) { return false; @@ -72,6 +70,8 @@ export default Controller.extend({ if (allocation) { return allocation.states.find(state => state.name === this.taskName); } + + return; } ), diff --git a/ui/app/controllers/jobs/index.js b/ui/app/controllers/jobs/index.js index 7052e773a..a3c1bbad7 100644 --- a/ui/app/controllers/jobs/index.js +++ b/ui/app/controllers/jobs/index.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */ import { inject as service } from '@ember/service'; import { alias, readOnly } from '@ember/object/computed'; import Controller, { inject as controller } from '@ember/controller'; @@ -32,8 +33,12 @@ export default Controller.extend(Sortable, Searchable, { sortProperty: 'modifyIndex', sortDescending: true, - searchProps: computed(() => ['id', 'name']), - fuzzySearchProps: computed(() => ['name']), + searchProps: computed(function() { + return ['id', 'name']; + }), + fuzzySearchProps: computed(function() { + return ['name']; + }), fuzzySearchEnabled: true, qpType: '', @@ -46,19 +51,23 @@ export default Controller.extend(Sortable, Searchable, { selectionDatacenter: selection('qpDatacenter'), selectionPrefix: selection('qpPrefix'), - optionsType: computed(() => [ - { key: 'batch', label: 'Batch' }, - { key: 'parameterized', label: 'Parameterized' }, - { key: 'periodic', label: 'Periodic' }, - { key: 'service', label: 'Service' }, - { key: 'system', label: 'System' }, - ]), + optionsType: computed(function() { + return [ + { key: 'batch', label: 'Batch' }, + { key: 'parameterized', label: 'Parameterized' }, + { key: 'periodic', label: 'Periodic' }, + { key: 'service', label: 'Service' }, + { key: 'system', label: 'System' }, + ]; + }), - optionsStatus: computed(() => [ - { key: 'pending', label: 'Pending' }, - { key: 'running', label: 'Running' }, - { key: 'dead', label: 'Dead' }, - ]), + optionsStatus: computed(function() { + return [ + { key: 'pending', label: 'Pending' }, + { key: 'running', label: 'Running' }, + { key: 'dead', label: 'Dead' }, + ]; + }), optionsDatacenter: computed('visibleJobs.[]', function() { const flatten = (acc, val) => acc.concat(val); @@ -67,6 +76,7 @@ export default Controller.extend(Sortable, Searchable, { // Remove any invalid datacenters from the query param/selection const availableDatacenters = Array.from(allDatacenters).compact(); scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects this.set( 'qpDatacenter', serialize(intersection(availableDatacenters, this.selectionDatacenter)) @@ -103,6 +113,7 @@ export default Controller.extend(Sortable, Searchable, { // Remove any invalid prefixes from the query param/selection const availablePrefixes = prefixes.mapBy('prefix'); scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects this.set('qpPrefix', serialize(intersection(availablePrefixes, this.selectionPrefix))); }); @@ -117,7 +128,7 @@ export default Controller.extend(Sortable, Searchable, { Visible jobs are those that match the selected namespace and aren't children of periodic or parameterized jobs. */ - visibleJobs: computed('model.[]', 'model.@each.parent', function() { + visibleJobs: computed('model.{[],@each.parent}', function() { // 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'); diff --git a/ui/app/controllers/jobs/job/allocations.js b/ui/app/controllers/jobs/job/allocations.js index 1ec7a40a2..0de958725 100644 --- a/ui/app/controllers/jobs/job/allocations.js +++ b/ui/app/controllers/jobs/job/allocations.js @@ -21,7 +21,9 @@ export default Controller.extend(Sortable, Searchable, WithNamespaceResetting, { job: alias('model'), - searchProps: computed(() => ['shortId', 'name', 'taskGroupName']), + searchProps: computed(function() { + return ['shortId', 'name', 'taskGroupName']; + }), allocations: computed('model.allocations.[]', function() { return this.get('model.allocations') || []; diff --git a/ui/app/controllers/jobs/job/task-group.js b/ui/app/controllers/jobs/job/task-group.js index 3e1a5d9c7..95127c4ed 100644 --- a/ui/app/controllers/jobs/job/task-group.js +++ b/ui/app/controllers/jobs/job/task-group.js @@ -22,7 +22,9 @@ export default Controller.extend(Sortable, Searchable, WithNamespaceResetting, { sortProperty: 'modifyIndex', sortDescending: true, - searchProps: computed(() => ['shortId', 'name']), + searchProps: computed(function() { + return ['shortId', 'name']; + }), allocations: computed('model.allocations.[]', function() { return this.get('model.allocations') || []; diff --git a/ui/app/mixins/searchable.js b/ui/app/mixins/searchable.js index cf39e758b..193679b32 100644 --- a/ui/app/mixins/searchable.js +++ b/ui/app/mixins/searchable.js @@ -22,9 +22,12 @@ import Fuse from 'fuse.js'; Properties provided: - listSearched: a subset of listToSearch of items that meet the search criteria */ +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ searchTerm: '', - listToSearch: computed(() => []), + listToSearch: computed(function() { + return []; + }), searchProps: null, exactMatchSearchProps: reads('searchProps'), @@ -83,11 +86,7 @@ export default Mixin.create({ if (this.exactMatchEnabled) { results.push( - ...exactMatchSearch( - searchTerm, - this.listToSearch, - this.exactMatchSearchProps - ) + ...exactMatchSearch(searchTerm, this.listToSearch, this.exactMatchSearchProps) ); } @@ -96,9 +95,7 @@ export default Mixin.create({ } if (this.regexEnabled) { - results.push( - ...regexSearch(searchTerm, this.listToSearch, this.regexSearchProps) - ); + results.push(...regexSearch(searchTerm, this.listToSearch, this.regexSearchProps)); } return results.uniq(); diff --git a/ui/app/mixins/sortable-factory.js b/ui/app/mixins/sortable-factory.js index e65d18f9b..c341b61ec 100644 --- a/ui/app/mixins/sortable-factory.js +++ b/ui/app/mixins/sortable-factory.js @@ -21,11 +21,14 @@ import { warn } from '@ember/debug'; export default function sortableFactory(properties, fromSortableMixin) { const eachProperties = properties.map(property => `listToSort.@each.${property}`); + // eslint-disable-next-line ember/no-new-mixins return Mixin.create({ // Override in mixin consumer sortProperty: null, sortDescending: true, - listToSort: computed(() => []), + listToSort: computed(function() { + return []; + }), _sortableFactoryWarningPrinted: false, @@ -44,6 +47,7 @@ export default function sortableFactory(properties, fromSortableMixin) { } warn(message, properties.length > 0, { id: 'nomad.no-sortable-properties' }); + // eslint-disable-next-line ember/no-side-effects this.set('_sortableFactoryWarningPrinted', true); } diff --git a/ui/app/mixins/window-resizable.js b/ui/app/mixins/window-resizable.js index a2daf1f85..d5d2eb159 100644 --- a/ui/app/mixins/window-resizable.js +++ b/ui/app/mixins/window-resizable.js @@ -3,18 +3,21 @@ import { run } from '@ember/runloop'; import { assert } from '@ember/debug'; import { on } from '@ember/object/evented'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ windowResizeHandler() { assert('windowResizeHandler needs to be overridden in the Component', false); }, setupWindowResize: on('didInsertElement', function() { - run.scheduleOnce('afterRender', this, () => { - this.set('_windowResizeHandler', this.windowResizeHandler.bind(this)); - window.addEventListener('resize', this._windowResizeHandler); - }); + run.scheduleOnce('afterRender', this, this.addResizeListener); }), + addResizeListener() { + this.set('_windowResizeHandler', this.windowResizeHandler.bind(this)); + window.addEventListener('resize', this._windowResizeHandler); + }, + removeWindowResize: on('willDestroyElement', function() { window.removeEventListener('resize', this._windowResizeHandler); }), diff --git a/ui/app/mixins/with-component-visibility-detection.js b/ui/app/mixins/with-component-visibility-detection.js index 2645ed2c8..57c341930 100644 --- a/ui/app/mixins/with-component-visibility-detection.js +++ b/ui/app/mixins/with-component-visibility-detection.js @@ -3,6 +3,7 @@ import Mixin from '@ember/object/mixin'; import { assert } from '@ember/debug'; import { on } from '@ember/object/evented'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ visibilityHandler() { assert('visibilityHandler needs to be overridden in the Component', false); diff --git a/ui/app/mixins/with-forbidden-state.js b/ui/app/mixins/with-forbidden-state.js index 64b8db81e..1e7b4e8fa 100644 --- a/ui/app/mixins/with-forbidden-state.js +++ b/ui/app/mixins/with-forbidden-state.js @@ -1,5 +1,6 @@ import Mixin from '@ember/object/mixin'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ setupController(controller) { if (this.isForbidden) { diff --git a/ui/app/mixins/with-model-error-handling.js b/ui/app/mixins/with-model-error-handling.js index 484a80148..248806a05 100644 --- a/ui/app/mixins/with-model-error-handling.js +++ b/ui/app/mixins/with-model-error-handling.js @@ -1,6 +1,7 @@ import Mixin from '@ember/object/mixin'; import notifyError from 'nomad-ui/utils/notify-error'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ model() { return this._super(...arguments).catch(notifyError(this)); diff --git a/ui/app/mixins/with-namespace-ids.js b/ui/app/mixins/with-namespace-ids.js index 3ba221751..bdbdb9969 100644 --- a/ui/app/mixins/with-namespace-ids.js +++ b/ui/app/mixins/with-namespace-ids.js @@ -1,6 +1,7 @@ import { inject as service } from '@ember/service'; import Mixin from '@ember/object/mixin'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ system: service(), diff --git a/ui/app/mixins/with-namespace-resetting.js b/ui/app/mixins/with-namespace-resetting.js index ab57f3e3e..04469ef1b 100644 --- a/ui/app/mixins/with-namespace-resetting.js +++ b/ui/app/mixins/with-namespace-resetting.js @@ -2,6 +2,7 @@ import { inject as controller } from '@ember/controller'; import { inject as service } from '@ember/service'; import Mixin from '@ember/object/mixin'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ system: service(), jobsController: controller('jobs'), diff --git a/ui/app/mixins/with-route-visibility-detection.js b/ui/app/mixins/with-route-visibility-detection.js index 8f58102c6..707146df8 100644 --- a/ui/app/mixins/with-route-visibility-detection.js +++ b/ui/app/mixins/with-route-visibility-detection.js @@ -3,6 +3,7 @@ import Mixin from '@ember/object/mixin'; import { assert } from '@ember/debug'; import { on } from '@ember/object/evented'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ visibilityHandler() { assert('visibilityHandler needs to be overridden in the Route', false); diff --git a/ui/app/mixins/with-watchers.js b/ui/app/mixins/with-watchers.js index ce89ec526..811a2c792 100644 --- a/ui/app/mixins/with-watchers.js +++ b/ui/app/mixins/with-watchers.js @@ -3,8 +3,11 @@ import { computed } from '@ember/object'; import { assert } from '@ember/debug'; import WithVisibilityDetection from './with-route-visibility-detection'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create(WithVisibilityDetection, { - watchers: computed(() => []), + watchers: computed(function() { + return []; + }), cancelAllWatchers() { this.watchers.forEach(watcher => { diff --git a/ui/app/models/job.js b/ui/app/models/job.js index f3afec1dc..5226b6bf9 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -1,4 +1,4 @@ -import { alias, equal, or, and } from '@ember/object/computed'; +import { alias, equal, or, and, mapBy } from '@ember/object/computed'; import { computed } from '@ember/object'; import Model from 'ember-data/model'; import attr from 'ember-data/attr'; @@ -61,8 +61,7 @@ export default Model.extend({ 'type', 'periodic', 'parameterized', - 'parent.periodic', - 'parent.parameterized', + 'parent.{periodic,parameterized}', function() { const type = this.type; @@ -131,9 +130,11 @@ export default Model.extend({ .uniq(); }), + allocationsUnhealthyDrivers: mapBy('allocations', 'unhealthyDrivers'), + // 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('allocations.@each.unhealthyDrivers.[]', function() { + unhealthyDrivers: computed('allocationsUnhealthyDrivers.[]', function() { return this.allocations .mapBy('unhealthyDrivers') .reduce((all, drivers) => { @@ -149,7 +150,7 @@ export default Model.extend({ hasPlacementFailures: and('latestFailureEvaluation', 'hasBlockedEvaluation'), - latestEvaluation: computed('evaluations.@each.modifyIndex', 'evaluations.isPending', function() { + latestEvaluation: computed('evaluations.{@each.modifyIndex,isPending}', function() { const evaluations = this.evaluations; if (!evaluations || evaluations.get('isPending')) { return null; @@ -157,21 +158,19 @@ export default Model.extend({ return evaluations.sortBy('modifyIndex').get('lastObject'); }), - latestFailureEvaluation: computed( - 'evaluations.@each.modifyIndex', - 'evaluations.isPending', - function() { - const evaluations = this.evaluations; - if (!evaluations || evaluations.get('isPending')) { - return null; - } - - const failureEvaluations = evaluations.filterBy('hasPlacementFailures'); - if (failureEvaluations) { - return failureEvaluations.sortBy('modifyIndex').get('lastObject'); - } + latestFailureEvaluation: computed('evaluations.{@each.modifyIndex,isPending}', function() { + const evaluations = this.evaluations; + if (!evaluations || evaluations.get('isPending')) { + return null; } - ), + + const failureEvaluations = evaluations.filterBy('hasPlacementFailures'); + if (failureEvaluations) { + return failureEvaluations.sortBy('modifyIndex').get('lastObject'); + } + + return; + }), supportsDeployments: equal('type', 'service'), @@ -180,6 +179,7 @@ export default Model.extend({ runningDeployment: computed('latestDeployment', 'latestDeployment.isRunning', function() { const latest = this.latestDeployment; if (latest.get('isRunning')) return latest; + return; }), fetchRawDefinition() { diff --git a/ui/app/models/node.js b/ui/app/models/node.js index eab3c1ff9..d272c7629 100644 --- a/ui/app/models/node.js +++ b/ui/app/models/node.js @@ -62,6 +62,8 @@ export default Model.extend({ if (allocation) { return allocation.modifyTime; } + + return; }), drivers: fragmentArray('node-driver'), diff --git a/ui/app/routes/clients.js b/ui/app/routes/clients.js index 42791cfb5..23c387284 100644 --- a/ui/app/routes/clients.js +++ b/ui/app/routes/clients.js @@ -8,12 +8,12 @@ export default Route.extend(WithForbiddenState, { store: service(), system: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { 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 9aaf2d516..30b181220 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 Route.extend(WithForbiddenState, { store: service(), - breadcrumbs: [ + 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 c870dc03a..fe49f7639 100644 --- a/ui/app/routes/csi/volumes.js +++ b/ui/app/routes/csi/volumes.js @@ -7,12 +7,12 @@ export default Route.extend(WithForbiddenState, { system: service(), store: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { label: 'Storage', args: ['csi.index'], }, - ], + ]), queryParams: { volumeNamespace: { diff --git a/ui/app/routes/jobs.js b/ui/app/routes/jobs.js index 08bbb26f9..42b2e1cb5 100644 --- a/ui/app/routes/jobs.js +++ b/ui/app/routes/jobs.js @@ -7,12 +7,12 @@ export default Route.extend(WithForbiddenState, { system: service(), store: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { label: 'Jobs', args: ['jobs.index'], }, - ], + ]), queryParams: { jobNamespace: { diff --git a/ui/app/routes/jobs/run.js b/ui/app/routes/jobs/run.js index 7bda3b103..3f45bf006 100644 --- a/ui/app/routes/jobs/run.js +++ b/ui/app/routes/jobs/run.js @@ -6,12 +6,12 @@ export default Route.extend({ store: service(), system: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { 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 6f1949334..62ee43b89 100644 --- a/ui/app/routes/servers.js +++ b/ui/app/routes/servers.js @@ -8,12 +8,12 @@ export default Route.extend(WithForbiddenState, { store: service(), system: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { 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 e545d42ac..78e5a49b7 100644 --- a/ui/app/serializers/volume.js +++ b/ui/app/serializers/volume.js @@ -6,7 +6,7 @@ export default ApplicationSerializer.extend({ externalId: 'ExternalID', }, - embeddedRelationships: ['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 diff --git a/ui/app/services/breadcrumbs.js b/ui/app/services/breadcrumbs.js index c820aee56..f01a7f680 100644 --- a/ui/app/services/breadcrumbs.js +++ b/ui/app/services/breadcrumbs.js @@ -9,7 +9,7 @@ export default Service.extend({ // 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', 'router.currentRouteName', function() { + breadcrumbs: computed('router.{currentURL,currentRouteName}', function() { const owner = getOwner(this); const allRoutes = (this.get('router.currentRouteName') || '') .split('.') diff --git a/ui/app/services/stats-trackers-registry.js b/ui/app/services/stats-trackers-registry.js index 5a603d016..751ac1d45 100644 --- a/ui/app/services/stats-trackers-registry.js +++ b/ui/app/services/stats-trackers-registry.js @@ -28,7 +28,9 @@ export default Service.extend({ // 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(() => registry), + registryRef: computed(function() { + return registry; + }), getTracker(resource) { if (!resource) return; diff --git a/ui/app/services/system.js b/ui/app/services/system.js index 24aea8291..bf71e4b3e 100644 --- a/ui/app/services/system.js +++ b/ui/app/services/system.js @@ -59,6 +59,7 @@ export default Service.extend({ 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. @@ -110,6 +111,7 @@ export default Service.extend({ 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); diff --git a/ui/app/services/token.js b/ui/app/services/token.js index 256c2646f..5e6dc70e6 100644 --- a/ui/app/services/token.js +++ b/ui/app/services/token.js @@ -43,6 +43,7 @@ export default Service.extend({ selfToken: computed('secret', 'fetchSelfToken.lastSuccessful.value', function() { if (this.secret) return this.get('fetchSelfToken.lastSuccessful.value'); + return; }), fetchSelfTokenPolicies: task(function*() { diff --git a/ui/app/services/watch-list.js b/ui/app/services/watch-list.js index 964736c8b..ab068650d 100644 --- a/ui/app/services/watch-list.js +++ b/ui/app/services/watch-list.js @@ -6,7 +6,9 @@ import Service from '@ember/service'; let list = {}; export default Service.extend({ - _list: computed(() => copy(list, true)), + _list: computed(function() { + return copy(list, true); + }), list: readOnly('_list'), diff --git a/ui/app/utils/classes/abstract-logger.js b/ui/app/utils/classes/abstract-logger.js index d90b785c1..8dc78ff1c 100644 --- a/ui/app/utils/classes/abstract-logger.js +++ b/ui/app/utils/classes/abstract-logger.js @@ -7,6 +7,7 @@ import queryString from 'query-string'; const MAX_OUTPUT_LENGTH = 50000; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ url: '', params: overridable(() => ({})), diff --git a/ui/app/utils/classes/abstract-stats-tracker.js b/ui/app/utils/classes/abstract-stats-tracker.js index ce39bfd79..dd51c11f4 100644 --- a/ui/app/utils/classes/abstract-stats-tracker.js +++ b/ui/app/utils/classes/abstract-stats-tracker.js @@ -4,6 +4,7 @@ import { assert } from '@ember/debug'; import { task, timeout } from 'ember-concurrency'; import jsonWithDefault from 'nomad-ui/utils/json-with-default'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ url: '', diff --git a/ui/app/utils/classes/stream-logger.js b/ui/app/utils/classes/stream-logger.js index 557f696fa..98e4fb4f3 100644 --- a/ui/app/utils/classes/stream-logger.js +++ b/ui/app/utils/classes/stream-logger.js @@ -8,9 +8,11 @@ import { fetchFailure } from './log'; export default EmberObject.extend(AbstractLogger, { reader: null, - additionalParams: computed(() => ({ - follow: true, - })), + additionalParams: computed(function() { + return { + follow: true, + }; + }), start() { return this.poll.perform(); diff --git a/ui/app/utils/fetch.js b/ui/app/utils/fetch.js index 460055137..0a4252434 100644 --- a/ui/app/utils/fetch.js +++ b/ui/app/utils/fetch.js @@ -1,4 +1,3 @@ -import Ember from 'ember'; import fetch from 'fetch'; import config from '../config/environment'; @@ -9,6 +8,6 @@ const mirageEnabled = config['ember-cli-mirage'] && config['ember-cli-mirage'].enabled !== false; -const fetchToUse = Ember.testing || mirageEnabled ? fetch : window.fetch || fetch; +const fetchToUse = mirageEnabled ? fetch : window.fetch || fetch; export default fetchToUse; diff --git a/ui/package.json b/ui/package.json index 32dd3eb6b..19488169e 100644 --- a/ui/package.json +++ b/ui/package.json @@ -95,6 +95,7 @@ "ember-test-selectors": "^2.1.0", "ember-truth-helpers": "^2.0.0", "eslint": "^5.16.0", + "eslint-plugin-ember": "^6.2.0", "eslint-plugin-node": "^9.0.1", "faker": "^4.1.0", "flat": "^4.0.0", diff --git a/ui/stories/charts/distribution-bar.stories.js b/ui/stories/charts/distribution-bar.stories.js index 5695d5c87..e84aff747 100644 --- a/ui/stories/charts/distribution-bar.stories.js +++ b/ui/stories/charts/distribution-bar.stories.js @@ -111,7 +111,7 @@ export let LiveUpdating = () => { clearInterval(this.timer); }, - distributionBarDataRotating: computed('timerTicks', () => { + distributionBarDataRotating: computed('timerTicks', function() { return [ { label: 'one', value: Math.round(Math.random() * 50) }, { label: 'two', value: Math.round(Math.random() * 50) }, diff --git a/ui/stories/charts/line-chart.stories.js b/ui/stories/charts/line-chart.stories.js index 7fa793a3a..4493a6b21 100644 --- a/ui/stories/charts/line-chart.stories.js +++ b/ui/stories/charts/line-chart.stories.js @@ -92,6 +92,8 @@ export let LiveData = () => { context: { controller: EmberObject.extend({ startTimer: on('init', function() { + this.lineChartLive = []; + this.set( 'timer', setInterval(() => { @@ -110,8 +112,6 @@ export let LiveData = () => { clearInterval(this.timer); }, - lineChartLive: [], - secondsFormat() { return date => moment(date).format('HH:mm:ss'); }, diff --git a/ui/stories/charts/stats-time-series.stories.js b/ui/stories/charts/stats-time-series.stories.js index 4976b9ce5..f78fab82f 100644 --- a/ui/stories/charts/stats-time-series.stories.js +++ b/ui/stories/charts/stats-time-series.stories.js @@ -102,11 +102,11 @@ export let HighLowComparison = () => { clearInterval(this.timer); }, - metricsHigh: computed(() => { + metricsHigh: computed(function() { return []; }), - metricsLow: computed(() => { + metricsLow: computed(function() { return []; }), diff --git a/ui/stories/components/table.stories.js b/ui/stories/components/table.stories.js index 662bfaeba..170257909 100644 --- a/ui/stories/components/table.stories.js +++ b/ui/stories/components/table.stories.js @@ -240,7 +240,7 @@ export let SortableColumns = () => { }) ), - sortedShortList: computed('controller.sortProperty', 'controller.sortDescending', function() { + sortedShortList: computed('controller.{sortProperty,sortDescending}', function() { let sorted = productMetadata.sortBy(this.get('controller.sortProperty') || 'name'); return this.get('controller.sortDescending') ? sorted.reverse() : sorted; }), @@ -278,7 +278,7 @@ export let MultiRow = () => { }) ), - sortedShortList: computed('controller.sortProperty', 'controller.sortDescending', function() { + sortedShortList: computed('controller.{sortProperty,sortDescending}', function() { let sorted = productMetadata.sortBy(this.get('controller.sortProperty') || 'name'); return this.get('controller.sortDescending') ? sorted.reverse() : sorted; }), diff --git a/ui/tests/integration/app-breadcrumbs-test.js b/ui/tests/integration/app-breadcrumbs-test.js index 32985d0f8..20d0fd725 100644 --- a/ui/tests/integration/app-breadcrumbs-test.js +++ b/ui/tests/integration/app-breadcrumbs-test.js @@ -11,7 +11,10 @@ module('Integration | Component | app breadcrumbs', function(hooks) { hooks.beforeEach(function() { const mockBreadcrumbs = Service.extend({ - breadcrumbs: [], + init() { + this._super(...arguments); + this.breadcrumbs = []; + }, }); this.owner.register('service:breadcrumbs', mockBreadcrumbs); diff --git a/ui/tests/integration/primary-metric-test.js b/ui/tests/integration/primary-metric-test.js index d454d4096..2e96436bf 100644 --- a/ui/tests/integration/primary-metric-test.js +++ b/ui/tests/integration/primary-metric-test.js @@ -31,8 +31,12 @@ module('Integration | Component | primary metric', function(hooks) { yield trackerSignalPauseSpy(); }), - cpu: computed(() => []), - memory: computed(() => []), + cpu: computed(function() { + return []; + }), + memory: computed(function() { + return []; + }), }); const mockStatsTrackersRegistry = Service.extend({ diff --git a/ui/tests/unit/abilities/allocation-test.js b/ui/tests/unit/abilities/allocation-test.js index 1190bfc84..174db388c 100644 --- a/ui/tests/unit/abilities/allocation-test.js +++ b/ui/tests/unit/abilities/allocation-test.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/avoid-leaking-state-in-ember-objects */ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import Service from '@ember/service'; diff --git a/ui/tests/unit/abilities/client-test.js b/ui/tests/unit/abilities/client-test.js index d037fb281..9b0ebd59d 100644 --- a/ui/tests/unit/abilities/client-test.js +++ b/ui/tests/unit/abilities/client-test.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/avoid-leaking-state-in-ember-objects */ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import Service from '@ember/service'; diff --git a/ui/tests/unit/abilities/job-test.js b/ui/tests/unit/abilities/job-test.js index e1e661d0c..bdc4ef5c6 100644 --- a/ui/tests/unit/abilities/job-test.js +++ b/ui/tests/unit/abilities/job-test.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/avoid-leaking-state-in-ember-objects */ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import Service from '@ember/service'; diff --git a/ui/tests/unit/mixins/searchable-test.js b/ui/tests/unit/mixins/searchable-test.js index 57bf662d3..6596d0b90 100644 --- a/ui/tests/unit/mixins/searchable-test.js +++ b/ui/tests/unit/mixins/searchable-test.js @@ -9,9 +9,12 @@ module('Unit | Mixin | Searchable', function(hooks) { hooks.beforeEach(function() { this.subject = function() { + // eslint-disable-next-line ember/no-new-mixins const SearchableObject = EmberObject.extend(Searchable, { source: null, - searchProps: computed(() => ['id', 'name']), + searchProps: computed(function() { + return ['id', 'name']; + }), listToSearch: alias('source'), }); @@ -193,9 +196,12 @@ module('Unit | Mixin | Searchable (with pagination)', function(hooks) { hooks.beforeEach(function() { this.subject = function() { + // eslint-disable-next-line ember/no-new-mixins const SearchablePaginatedObject = EmberObject.extend(Searchable, { source: null, - searchProps: computed(() => ['id', 'name']), + searchProps: computed(function() { + return ['id', 'name']; + }), listToSearch: alias('source'), currentPage: 1, }); diff --git a/ui/tests/unit/utils/log-test.js b/ui/tests/unit/utils/log-test.js index e69b8c105..072067d0e 100644 --- a/ui/tests/unit/utils/log-test.js +++ b/ui/tests/unit/utils/log-test.js @@ -10,11 +10,11 @@ import { settled } from '@ember/test-helpers'; let startSpy, stopSpy, initSpy, fetchSpy; const MockStreamer = EmberObject.extend({ - poll: { - isRunning: false, - }, - init() { + this.poll = { + isRunning: false, + }; + initSpy(...arguments); }, diff --git a/ui/yarn.lock b/ui/yarn.lock index e5bf949e8..b3a5edbe5 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1279,6 +1279,11 @@ ember-cli-typescript "^2.0.2" inflection "1.12.0" +"@ember-data/rfc395-data@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@ember-data/rfc395-data/-/rfc395-data-0.0.4.tgz#ecb86efdf5d7733a76ff14ea651a1b0ed1f8a843" + integrity sha512-tGRdvgC9/QMQSuSuJV45xoyhI0Pzjm7A9o/MVVA3HakXIImJbbzx/k/6dO9CUEQXIyS2y0fW6C1XaYOG7rY0FQ== + "@ember-data/serializer@3.12.4": version "3.12.4" resolved "https://registry.yarnpkg.com/@ember-data/serializer/-/serializer-3.12.4.tgz#e3ae7143f0cd736722daa3a640e11dc07c8aef5a" @@ -7427,6 +7432,11 @@ ember-responsive@^3.0.4: dependencies: ember-cli-babel "^6.6.0" +ember-rfc176-data@^0.3.11: + 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== + ember-rfc176-data@^0.3.12, ember-rfc176-data@^0.3.9: version "0.3.12" resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.12.tgz#90d82878e69e2ac9a5438e8ce14d12c6031c5bd2" @@ -7714,6 +7724,15 @@ escodegen@^1.11.0: optionalDependencies: source-map "~0.6.1" +eslint-plugin-ember@^6.2.0: + version "6.10.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-ember/-/eslint-plugin-ember-6.10.1.tgz#ca7a5cc28b91a247c31b1686421a66281467f238" + integrity sha512-RZI0+UoR4xeD6UE3KQCUwbN2nZOIIPaFCCXqBIRXDr0rFuwvknAHqYtDPJVZicvTzNHa4TEZvAKqfbE8t7SztQ== + dependencies: + "@ember-data/rfc395-data" "^0.0.4" + ember-rfc176-data "^0.3.11" + snake-case "^2.1.0" + eslint-plugin-es@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz#12acae0f4953e76ba444bfd1b2271081ac620998" @@ -13960,6 +13979,13 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= + dependencies: + no-case "^2.2.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"