Merge pull request #5544 from hashicorp/f-ui-ember-v3

UI: Ember 2.18 → 3.4 Take two
This commit is contained in:
Michael Lange 2019-04-10 16:04:41 -07:00 committed by GitHub
commit d57aeeddd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
204 changed files with 13281 additions and 13349 deletions

5
.gitignore vendored
View File

@ -87,6 +87,11 @@ rkt-*
/ui/testem.log
.ignore
# ember-try
/ui/.node_modules.ember-try/
/ui/bower.json.ember-try
/ui/package.json.ember-try
# generated routes file
command/agent/bindata_assetfs.go

View File

@ -71,13 +71,14 @@ before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ -z "$SKIP_NOMAD_TESTS" ]]; then sudo -E bash ./scripts/travis-mac-priv.sh ; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ -z "$SKIP_NOMAD_TESTS" ]]; then sudo -E bash ./scripts/travis-linux.sh ; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$RUN_STATIC_CHECKS" ]]; then sudo -E bash ./scripts/vagrant-linux-priv-protoc.sh; fi
- if [[ "$RUN_UI_TESTS" ]]; then curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.0.1 ; fi
- if [[ "$RUN_UI_TESTS" ]]; then curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.15.2 ; fi
- if [[ "$RUN_UI_TESTS" ]]; then export PATH="$HOME/.yarn/bin:$PATH" ; fi
- echo "Travis Node Version" && node -v
install:
- if [[ -z "$SKIP_NOMAD_TESTS" ]] || [[ "$RUN_STATIC_CHECKS" ]]; then make deps; fi
- if [[ "$RUN_STATIC_CHECKS" ]]; then make lint-deps ; fi
- if [[ "$RUN_UI_TESTS" ]]; then . $HOME/.nvm/nvm.sh && cd ui && nvm use && cd .. ; fi
- if [[ "$RUN_UI_TESTS" ]]; then . $HOME/.nvm/nvm.sh && cd ui && nvm install && nvm use && cd .. ; fi
script:
- sudo -E "PATH=$PATH" GOTESTSUM_JUNITFILE=/tmp/results.xml make travis

View File

@ -9,12 +9,12 @@ export NVM_DIR="/home/vagrant/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
# Install Node, Ember CLI, and Phantom for UI development
nvm install 8.11.2
nvm alias default 8.11.2
nvm install 10
nvm alias default 10
npm install -g ember-cli
# Install Yarn for front-end dependency management
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.7.0
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.15.2
# Install Chrome for running tests (in headless mode)
wget -qO- - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -

View File

@ -1 +1,22 @@
mirage/
# unconventional js
/blueprints/*/files/
/vendor/
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/coverage/
!.*
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try

View File

@ -8,11 +8,8 @@ module.exports = {
},
extends: 'eslint:recommended',
parserOptions: {
ecmaVersion: 2017,
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
experimentalObjectRestSpread: true,
},
},
rules: {
indent: ['error', 2, { SwitchCase: 1 }],
@ -26,4 +23,26 @@ module.exports = {
},
],
},
overrides: [
// node files
{
files: [
'.eslintrc.js',
'.template-lintrc.js',
'ember-cli-build.js',
'testem.js',
'blueprints/*/index.js',
'config/**/*.js',
'lib/*/index.js',
],
parserOptions: {
sourceType: 'script',
ecmaVersion: 2015,
},
env: {
browser: false,
node: true,
},
},
],
};

View File

@ -1 +1 @@
8
10

40
ui/.template-lintrc.js Normal file
View File

@ -0,0 +1,40 @@
'use strict';
module.exports = {
extends: 'recommended',
rules: {
// should definitely move to template only
// glimmer components for this one
'no-partial': false,
// these need to be looked into, but
// may be a bigger change
'no-invalid-interactive': false,
'simple-unless': false,
'self-closing-void-elements': false,
'no-unnecessary-concat': false,
'no-quoteless-attributes': false,
'no-nested-interactive': false,
// Only used in list-pager, which can be replaced with
// an angle-bracket component
'no-attrs-in-components': false,
// Used in practice with charts. Ideally this would be true
// except for a whitelist of chart files.
'no-inline-styles': false,
// not sure we'll ever want these on,
// would be nice but if prettier isn't doing
// it for us, then not sure it's worth it
'attribute-indentation': false,
'block-indentation': false,
quotes: false,
},
ignore: [
'app/templates/components/freestyle/**',
'nomad-ui/templates/components/freestyle/**'
]
};

View File

@ -49,6 +49,16 @@ Nomad UI tests can be run independently of Nomad golang tests.
* `ember test` (single run, headless browser)
* `ember test --server` (watches for changes, runs in a full browser)
### Linting
Linting should happen automatically in your editor and when committing changes, but it can also be invoked manually.
* `npm run lint:hbs`
* `npm run lint:js`
* `npm run lint:js -- --fix`
### Building
Typically `make release` or `make dev-ui` will be the desired build workflow, but in the event that build artifacts need to be inspected, `ember build` will output compiled files in `ui/dist`.

View File

@ -78,7 +78,7 @@ export default Watchable.extend({
plan(job) {
const jobId = job.get('id');
const store = this.get('store');
const store = this.store;
const url = addToPath(this.urlForFindRecord(jobId, 'job'), '/plan');
return this.ajax(url, 'POST', {

View File

@ -8,7 +8,7 @@ export default ApplicationAdapter.extend({
findSelf() {
return this.ajax(`${this.buildURL()}/token/self`, 'GET').then(token => {
const store = this.get('store');
const store = this.store;
store.pushPayload('token', {
tokens: [token],
});

View File

@ -42,9 +42,9 @@ export default ApplicationAdapter.extend({
if (previousBeforeSend) {
previousBeforeSend(...arguments);
}
this.get('xhrs').track(key, jqXHR);
this.xhrs.track(key, jqXHR);
jqXHR.always(() => {
this.get('xhrs').remove(key, jqXHR);
this.xhrs.remove(key, jqXHR);
});
};
@ -60,7 +60,7 @@ export default ApplicationAdapter.extend({
const url = this.urlForFindAll(type.modelName);
if (get(snapshotRecordArray || {}, 'adapterOptions.watch')) {
params.index = this.get('watchList').getIndexFor(url);
params.index = this.watchList.getIndexFor(url);
}
return this.ajax(url, 'GET', {
@ -73,7 +73,7 @@ export default ApplicationAdapter.extend({
params = assign(queryString.parse(params) || {}, this.buildQuery(), additionalParams);
if (get(snapshot || {}, 'adapterOptions.watch')) {
params.index = this.get('watchList').getIndexFor(url);
params.index = this.watchList.getIndexFor(url);
}
return this.ajax(url, 'GET', {
@ -97,7 +97,7 @@ export default ApplicationAdapter.extend({
let params = {};
if (watch) {
params.index = this.get('watchList').getIndexFor(url);
params.index = this.watchList.getIndexFor(url);
}
// Avoid duplicating existing query params by passing them to ajax
@ -113,7 +113,7 @@ export default ApplicationAdapter.extend({
data: params,
}).then(
json => {
const store = this.get('store');
const store = this.store;
const normalizeMethod =
relationship.kind === 'belongsTo'
? 'normalizeFindBelongsToResponse'
@ -138,7 +138,7 @@ export default ApplicationAdapter.extend({
// case sensitive.
const newIndex = headers['x-nomad-index'] || headers['X-Nomad-Index'];
if (newIndex) {
this.get('watchList').setIndexFor(requestData.url, newIndex);
this.watchList.setIndexFor(requestData.url, newIndex);
}
return this._super(...arguments);
@ -149,7 +149,7 @@ export default ApplicationAdapter.extend({
return;
}
const url = this.urlForFindRecord(id, modelName);
this.get('xhrs').cancel(`GET ${url}`);
this.xhrs.cancel(`GET ${url}`);
},
cancelFindAll(modelName) {
@ -161,7 +161,7 @@ export default ApplicationAdapter.extend({
if (params) {
url = `${url}?${params}`;
}
this.get('xhrs').cancel(`GET ${url}`);
this.xhrs.cancel(`GET ${url}`);
},
cancelReloadRelationship(model, relationshipName) {
@ -175,7 +175,7 @@ export default ApplicationAdapter.extend({
);
} else {
const url = model[relationship.kind](relationship.key).link();
this.get('xhrs').cancel(`GET ${url}`);
this.xhrs.cancel(`GET ${url}`);
}
},
});

View File

@ -30,8 +30,8 @@ export default Component.extend({
if (!this.get('allocation.isRunning')) return;
return AllocationStatsTracker.create({
fetch: url => this.get('token').authorizedRequest(url),
allocation: this.get('allocation'),
fetch: url => this.token.authorizedRequest(url),
allocation: this.allocation,
});
}),
@ -41,22 +41,22 @@ export default Component.extend({
onClick() {},
click(event) {
lazyClick([this.get('onClick'), event]);
lazyClick([this.onClick, event]);
},
didReceiveAttrs() {
const allocation = this.get('allocation');
const allocation = this.allocation;
if (allocation) {
run.scheduleOnce('afterRender', this, qualifyAllocation);
} else {
this.get('fetchStats').cancelAll();
this.fetchStats.cancelAll();
}
},
fetchStats: task(function*() {
do {
if (this.get('stats')) {
if (this.stats) {
try {
yield this.get('stats.poll').perform();
this.set('statsError', false);
@ -66,14 +66,14 @@ export default Component.extend({
}
yield timeout(500);
} while (this.get('enablePolling'));
} while (this.enablePolling);
}).drop(),
});
function qualifyAllocation() {
const allocation = this.get('allocation');
const allocation = this.allocation;
return allocation.reload().then(() => {
this.get('fetchStats').perform();
this.fetchStats.perform();
// Make sure that the job record in the store for this allocation
// is complete and not a partial from the list endpoint

View File

@ -11,11 +11,11 @@ export default DistributionBar.extend({
data: computed(
'allocationContainer.{queuedAllocs,completeAllocs,failedAllocs,runningAllocs,startingAllocs}',
function() {
if (!this.get('allocationContainer')) {
if (!this.allocationContainer) {
return [];
}
const allocs = this.get('allocationContainer').getProperties(
const allocs = this.allocationContainer.getProperties(
'queuedAllocs',
'completeAllocs',
'failedAllocs',

View File

@ -9,11 +9,11 @@ export default DistributionBar.extend({
'data-test-children-status-bar': true,
data: computed('job.{pendingChildren,runningChildren,deadChildren}', function() {
if (!this.get('job')) {
if (!this.job) {
return [];
}
const children = this.get('job').getProperties(
const children = this.job.getProperties(
'pendingChildren',
'runningChildren',
'deadChildren'

View File

@ -15,32 +15,32 @@ export default Component.extend(WithVisibilityDetection, {
onClick() {},
click(event) {
lazyClick([this.get('onClick'), event]);
lazyClick([this.onClick, event]);
},
didReceiveAttrs() {
// Reload the node in order to get detail information
const node = this.get('node');
const node = this.node;
if (node) {
node.reload().then(() => {
this.get('watch').perform(node, 100);
this.watch.perform(node, 100);
});
}
},
visibilityHandler() {
if (document.hidden) {
this.get('watch').cancelAll();
this.watch.cancelAll();
} else {
const node = this.get('node');
const node = this.node;
if (node) {
this.get('watch').perform(node, 100);
this.watch.perform(node, 100);
}
}
},
willDestroy() {
this.get('watch').cancelAll();
this.watch.cancelAll();
this._super(...arguments);
},

View File

@ -2,7 +2,8 @@ import Component from '@ember/component';
import { computed, observer, set } from '@ember/object';
import { run } from '@ember/runloop';
import { assign } from '@ember/polyfills';
import { guidFor, copy } from '@ember/object/internals';
import { guidFor } from '@ember/object/internals';
import { copy } from 'ember-copy';
import d3 from 'd3-selection';
import 'd3-transition';
import WindowResizable from '../mixins/window-resizable';
@ -23,7 +24,7 @@ export default Component.extend(WindowResizable, {
maskId: null,
_data: computed('data', function() {
const data = copy(this.get('data'), true);
const data = copy(this.data, true);
const sum = data.mapBy('value').reduce(sumAggregate, 0);
return data.map(({ label, value, className, layers }, index) => ({
@ -73,7 +74,7 @@ export default Component.extend(WindowResizable, {
// prettier-ignore
/* eslint-disable */
renderChart() {
const { chart, _data, isNarrow } = this.getProperties('chart', '_data', 'isNarrow');
const { chart, _data, isNarrow } = this;
const width = this.$('svg').width();
const filteredData = _data.filter(d => d.value > 0);
filteredData.forEach((d, index) => {
@ -89,7 +90,7 @@ export default Component.extend(WindowResizable, {
.append('g')
.on('mouseenter', d => {
run(() => {
const slices = this.get('slices');
const slices = this.slices;
const slice = slices.filter(datum => datum.label === d.label);
slices.classed('active', false).classed('inactive', true);
slice.classed('active', true).classed('inactive', false);
@ -109,7 +110,7 @@ export default Component.extend(WindowResizable, {
slices = slices.merge(slicesEnter);
slices.attr('class', d => {
const className = d.className || `slice-${_data.indexOf(d)}`
const activeDatum = this.get('activeDatum');
const activeDatum = this.activeDatum;
const isActive = activeDatum && activeDatum.label === d.label;
const isInactive = activeDatum && activeDatum.label !== d.label;
return [ className, isActive && 'active', isInactive && 'inactive' ].compact().join(' ');
@ -148,7 +149,7 @@ export default Component.extend(WindowResizable, {
.attr('width', setWidth)
.attr('x', setOffset)
.attr('y', () => isNarrow ? '50%' : 0)
.attr('clip-path', `url(#${this.get('maskId')})`)
.attr('clip-path', `url(#${this.maskId})`)
.attr('height', () => isNarrow ? '6px' : '100%')
.attr('transform', () => isNarrow ? 'translate(0, -3)' : '')
.merge(layers)
@ -159,7 +160,7 @@ export default Component.extend(WindowResizable, {
.attr('x', setOffset)
if (isNarrow) {
d3.select(this.get('element')).select('.mask')
d3.select(this.element).select('.mask')
.attr('height', '6px')
.attr('y', '50%');
}

View File

@ -14,7 +14,7 @@ export default Component.extend({
}.on('init'),
willDestroy() {
clearInterval(this.get('timer'));
clearInterval(this.timer);
},
distributionBarDatum: computed(() => {

View File

@ -11,7 +11,7 @@ export default Component.extend({
setInterval(() => {
this.incrementProperty('timerTicks');
const ref = this.get('lineChartLive');
const ref = this.lineChartLive;
ref.addObject({ ts: Date.now(), val: Math.random() * 30 + 20 });
if (ref.length > 60) {
ref.splice(0, ref.length - 60);
@ -21,7 +21,7 @@ export default Component.extend({
}.on('init'),
willDestroy() {
clearInterval(this.get('timer'));
clearInterval(this.timer);
},
lineChartData: computed(() => {

View File

@ -14,7 +14,7 @@ export default Component.extend({
}.on('init'),
willDestroy() {
clearInterval(this.get('timer'));
clearInterval(this.timer);
},
denominator: computed('timerTicks', function() {
@ -26,7 +26,7 @@ export default Component.extend({
}),
numerator: computed('denominator', 'percentage', function() {
return Math.round(this.get('denominator') * this.get('percentage') * 100) / 100;
return Math.round(this.denominator * this.percentage * 100) / 100;
}),
liveDetails: computed('denominator', 'numerator', 'percentage', function() {

View File

@ -10,14 +10,14 @@ export default Component.extend({
this.set(
'timer',
setInterval(() => {
const metricsHigh = this.get('metricsHigh');
const metricsHigh = this.metricsHigh;
const prev = metricsHigh.length ? metricsHigh[metricsHigh.length - 1].value : 0.9;
this.appendTSValue(
metricsHigh,
Math.min(Math.max(prev + Math.random() * 0.05 - 0.025, 0.5), 1)
);
const metricsLow = this.get('metricsLow');
const metricsLow = this.metricsLow;
const prev2 = metricsLow.length ? metricsLow[metricsLow.length - 1].value : 0.1;
this.appendTSValue(
metricsLow,
@ -39,7 +39,7 @@ export default Component.extend({
},
willDestroy() {
clearInterval(this.get('timer'));
clearInterval(this.timer);
},
metricsHigh: computed(() => {

View File

@ -107,12 +107,12 @@ export default Component.extend({
],
filteredShortList: computed('searchTerm', 'shortList.[]', function() {
const term = this.get('searchTerm').toLowerCase();
return this.get('shortList').filter(product => product.name.toLowerCase().includes(term));
const term = this.searchTerm.toLowerCase();
return this.shortList.filter(product => product.name.toLowerCase().includes(term));
}),
sortedShortList: computed('shortList.[]', 'sortProperty', 'sortDescending', function() {
const sorted = this.get('shortList').sortBy(this.get('sortProperty'));
return this.get('sortDescending') ? sorted.reverse() : sorted;
const sorted = this.shortList.sortBy(this.sortProperty);
return this.sortDescending ? sorted.reverse() : sorted;
}),
});

View File

@ -37,7 +37,7 @@ export default Component.extend({
gotoJobsForNamespace(namespace) {
if (!namespace || !namespace.get('id')) return;
this.get('router').transitionTo('jobs', {
this.router.transitionTo('jobs', {
queryParams: { namespace: namespace.get('id') },
});
},

View File

@ -9,13 +9,13 @@ export default Component.extend({
deployments: computed(() => []),
sortedDeployments: computed('deployments.@each.versionSubmitTime', function() {
return this.get('deployments')
return this.deployments
.sortBy('versionSubmitTime')
.reverse();
}),
annotatedDeployments: computed('sortedDeployments.@each.version', function() {
const deployments = this.get('sortedDeployments');
const deployments = this.sortedDeployments;
return deployments.map((deployment, index) => {
const meta = {};

View File

@ -16,7 +16,7 @@ export default Component.extend({
onSubmit() {},
context: computed({
get() {
return this.get('_context');
return this._context;
},
set(key, value) {
const allowedValues = ['new', 'edit'];
@ -39,14 +39,14 @@ export default Component.extend({
showEditorMessage: localStorageProperty('nomadMessageJobEditor', true),
stage: computed('planOutput', function() {
return this.get('planOutput') ? 'plan' : 'editor';
return this.planOutput ? 'plan' : 'editor';
}),
plan: task(function*() {
this.reset();
try {
yield this.get('job').parse();
yield this.job.parse();
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not parse input';
this.set('parseError', error);
@ -55,7 +55,7 @@ export default Component.extend({
}
try {
const plan = yield this.get('job').plan();
const plan = yield this.job.plan();
this.set('planOutput', plan);
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not plan job';
@ -66,10 +66,10 @@ export default Component.extend({
submit: task(function*() {
try {
if (this.get('context') === 'new') {
yield this.get('job').run();
if (this.context === 'new') {
yield this.job.run();
} else {
yield this.get('job').update();
yield this.job.update();
}
const id = this.get('job.plainId');
@ -78,7 +78,7 @@ export default Component.extend({
this.reset();
// Treat the job as ephemeral and only provide ID parts.
this.get('onSubmit')(id, namespace);
this.onSubmit(id, namespace);
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not submit job';
this.set('runError', error);

View File

@ -7,7 +7,7 @@ export default PeriodicChildJobPage.extend({
payloadJSON: computed('payload', function() {
let json;
try {
json = JSON.parse(this.get('payload'));
json = JSON.parse(this.payload);
} catch (e) {
// Swallow error and fall back to plain text rendering
}

View File

@ -18,7 +18,7 @@ export default Component.extend({
if (!message || message === 'Forbidden') {
message = 'Your ACL token does not grant permission to promote deployments.';
}
this.get('handleError')({
this.handleError({
title: 'Could Not Promote Deployment',
description: message,
});

View File

@ -12,12 +12,12 @@ export default Component.extend({
stopJob: task(function*() {
try {
const job = this.get('job');
const job = this.job;
yield job.stop();
// Eagerly update the job status to avoid flickering
this.job.set('status', 'dead');
} catch (err) {
this.get('handleError')({
this.handleError({
title: 'Could Not Stop Job',
description: 'Your ACL token does not grant permission to stop jobs.',
});
@ -25,7 +25,7 @@ export default Component.extend({
}),
startJob: task(function*() {
const job = this.get('job');
const job = this.job;
const definition = yield job.fetchRawDefinition();
delete definition.Stop;
@ -42,7 +42,7 @@ export default Component.extend({
message = 'Your ACL token does not grant permission to stop jobs.';
}
this.get('handleError')({
this.handleError({
title: 'Could Not Start Job',
description: message,
});

View File

@ -3,7 +3,7 @@ import { computed } from '@ember/object';
export default AbstractJobPage.extend({
breadcrumbs: computed('job.{name,id}', 'job.parent.{name,id}', function() {
const job = this.get('job');
const job = this.job;
const parent = this.get('job.parent');
return [

View File

@ -8,7 +8,7 @@ export default AbstractJobPage.extend({
actions: {
forceLaunch() {
this.get('job')
this.job
.forcePeriodic()
.catch(() => {
this.set('errorMessage', {

View File

@ -13,6 +13,6 @@ export default Component.extend({
onClick() {},
click(event) {
lazyClick([this.get('onClick'), event]);
lazyClick([this.onClick, event]);
},
});

View File

@ -12,7 +12,7 @@ export default Component.extend({
verbose: true,
annotatedVersions: computed('versions.[]', function() {
const versions = this.get('versions')
const versions = this.versions
.sortBy('submitTime')
.reverse();
return versions.map((version, index) => {

View File

@ -6,6 +6,6 @@ export default Component.extend({
json: null,
jsonStr: computed('json', function() {
return JSON.stringify(this.get('json'), null, 2);
return JSON.stringify(this.json, null, 2);
}),
});

View File

@ -58,20 +58,20 @@ export default Component.extend(WindowResizable, {
activeDatum: null,
activeDatumLabel: computed('activeDatum', function() {
const datum = this.get('activeDatum');
const datum = this.activeDatum;
if (!datum) return;
const x = datum[this.get('xProp')];
return this.xFormat(this.get('timeseries'))(x);
const x = datum[this.xProp];
return this.xFormat(this.timeseries)(x);
}),
activeDatumValue: computed('activeDatum', function() {
const datum = this.get('activeDatum');
const datum = this.activeDatum;
if (!datum) return;
const y = datum[this.get('yProp')];
const y = datum[this.yProp];
return this.yFormat()(y);
}),
@ -88,19 +88,19 @@ export default Component.extend(WindowResizable, {
tooltipStyle: styleStringProperty('tooltipPosition'),
xScale: computed('data.[]', 'xProp', 'timeseries', 'yAxisOffset', function() {
const xProp = this.get('xProp');
const scale = this.get('timeseries') ? d3Scale.scaleTime() : d3Scale.scaleLinear();
const data = this.get('data');
const xProp = this.xProp;
const scale = this.timeseries ? d3Scale.scaleTime() : d3Scale.scaleLinear();
const data = this.data;
const domain = data.length ? d3Array.extent(this.get('data'), d => d[xProp]) : [0, 1];
const domain = data.length ? d3Array.extent(this.data, d => d[xProp]) : [0, 1];
scale.rangeRound([10, this.get('yAxisOffset')]).domain(domain);
scale.rangeRound([10, this.yAxisOffset]).domain(domain);
return scale;
}),
xRange: computed('data.[]', 'xFormat', 'xProp', 'timeseries', function() {
const { xProp, timeseries, data } = this.getProperties('xProp', 'timeseries', 'data');
const { xProp, timeseries, data } = this;
const range = d3Array.extent(data, d => d[xProp]);
const formatter = this.xFormat(timeseries);
@ -108,40 +108,40 @@ export default Component.extend(WindowResizable, {
}),
yRange: computed('data.[]', 'yFormat', 'yProp', function() {
const yProp = this.get('yProp');
const range = d3Array.extent(this.get('data'), d => d[yProp]);
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() {
const yProp = this.get('yProp');
let max = d3Array.max(this.get('data'), d => d[yProp]) || 1;
const yProp = this.yProp;
let max = d3Array.max(this.data, d => d[yProp]) || 1;
if (max > 1) {
max = nice(max);
}
return d3Scale
.scaleLinear()
.rangeRound([this.get('xAxisOffset'), 10])
.rangeRound([this.xAxisOffset, 10])
.domain([0, max]);
}),
xAxis: computed('xScale', function() {
const formatter = this.xFormat(this.get('timeseries'));
const formatter = this.xFormat(this.timeseries);
return d3Axis
.axisBottom()
.scale(this.get('xScale'))
.scale(this.xScale)
.ticks(5)
.tickFormat(formatter);
}),
yTicks: computed('xAxisOffset', function() {
const height = this.get('xAxisOffset');
const height = this.xAxisOffset;
const tickCount = Math.ceil(height / 120) * 2 + 1;
const domain = this.get('yScale').domain();
const domain = this.yScale.domain();
const ticks = lerp(domain, tickCount);
return domain[1] - domain[0] > 1 ? nice(ticks) : ticks;
}),
@ -151,20 +151,20 @@ export default Component.extend(WindowResizable, {
return d3Axis
.axisRight()
.scale(this.get('yScale'))
.tickValues(this.get('yTicks'))
.scale(this.yScale)
.tickValues(this.yTicks)
.tickFormat(formatter);
}),
yGridlines: computed('yScale', function() {
// The first gridline overlaps the x-axis, so remove it
const [, ...ticks] = this.get('yTicks');
const [, ...ticks] = this.yTicks;
return d3Axis
.axisRight()
.scale(this.get('yScale'))
.scale(this.yScale)
.tickValues(ticks)
.tickSize(-this.get('yAxisOffset'))
.tickSize(-this.yAxisOffset)
.tickFormat('');
}),
@ -185,20 +185,15 @@ export default Component.extend(WindowResizable, {
}),
xAxisOffset: computed('height', 'xAxisHeight', function() {
return this.get('height') - this.get('xAxisHeight');
return this.height - this.xAxisHeight;
}),
yAxisOffset: computed('width', 'yAxisWidth', function() {
return this.get('width') - this.get('yAxisWidth');
return this.width - this.yAxisWidth;
}),
line: computed('data.[]', 'xScale', 'yScale', function() {
const { xScale, yScale, xProp, yProp } = this.getProperties(
'xScale',
'yScale',
'xProp',
'yProp'
);
const { xScale, yScale, xProp, yProp } = this;
const line = d3Shape
.line()
@ -206,16 +201,11 @@ export default Component.extend(WindowResizable, {
.x(d => xScale(d[xProp]))
.y(d => yScale(d[yProp]));
return line(this.get('data'));
return line(this.data);
}),
area: computed('data.[]', 'xScale', 'yScale', function() {
const { xScale, yScale, xProp, yProp } = this.getProperties(
'xScale',
'yScale',
'xProp',
'yProp'
);
const { xScale, yScale, xProp, yProp } = this;
const area = d3Shape
.area()
@ -224,7 +214,7 @@ export default Component.extend(WindowResizable, {
.y0(yScale(0))
.y1(d => yScale(d[yProp]));
return area(this.get('data'));
return area(this.data);
}),
didInsertElement() {
@ -258,13 +248,7 @@ export default Component.extend(WindowResizable, {
},
updateActiveDatum(mouseX) {
const { xScale, xProp, yScale, yProp, data } = this.getProperties(
'xScale',
'xProp',
'yScale',
'yProp',
'data'
);
const { xScale, xProp, yScale, yProp, data } = this;
if (!data || !data.length) return;
@ -318,17 +302,17 @@ export default Component.extend(WindowResizable, {
// axis, the axes themselves are recomputed and need to
// be re-rendered.
this.mountD3Elements();
if (this.get('isActive')) {
this.updateActiveDatum(this.get('latestMouseX'));
if (this.isActive) {
this.updateActiveDatum(this.latestMouseX);
}
});
},
mountD3Elements() {
if (!this.get('isDestroyed') && !this.get('isDestroying')) {
d3.select(this.element.querySelector('.x-axis')).call(this.get('xAxis'));
d3.select(this.element.querySelector('.y-axis')).call(this.get('yAxis'));
d3.select(this.element.querySelector('.y-gridlines')).call(this.get('yGridlines'));
if (!this.isDestroyed && !this.isDestroying) {
d3.select(this.element.querySelector('.x-axis')).call(this.xAxis);
d3.select(this.element.querySelector('.y-axis')).call(this.yAxis);
d3.select(this.element.querySelector('.y-gridlines')).call(this.yGridlines);
}
},

View File

@ -11,12 +11,12 @@ export default Component.extend({
startExpanded: false,
decoratedSource: computed('source.[]', function() {
const stateCache = this.get('stateCache');
const key = this.get('key');
const stateCache = this.stateCache;
const key = this.key;
const deepKey = `item.${key}`;
const startExpanded = this.get('startExpanded');
const startExpanded = this.startExpanded;
const decoratedSource = this.get('source').map(item => {
const decoratedSource = this.source.map(item => {
const cacheItem = stateCache.findBy(deepKey, get(item, key));
return {
item,

View File

@ -8,19 +8,19 @@ export default Component.extend({
spread: 2,
startsAt: computed('size', 'page', function() {
return (this.get('page') - 1) * this.get('size') + 1;
return (this.page - 1) * this.size + 1;
}),
endsAt: computed('source.[]', 'size', 'page', function() {
return Math.min(this.get('page') * this.get('size'), this.get('source.length'));
return Math.min(this.page * this.size, this.get('source.length'));
}),
lastPage: computed('source.[]', 'size', function() {
return Math.ceil(this.get('source.length') / this.get('size'));
return Math.ceil(this.get('source.length') / this.size);
}),
pageLinks: computed('source.[]', 'page', 'spread', function() {
const { spread, page, lastPage } = this.getProperties('spread', 'page', 'lastPage');
const { spread, page, lastPage } = this;
// When there is only one page, don't bother with page links
if (lastPage === 1) {
@ -38,8 +38,8 @@ export default Component.extend({
}),
list: computed('source.[]', 'page', 'size', function() {
const size = this.get('size');
const start = (this.get('page') - 1) * size;
return this.get('source').slice(start, start + size);
const size = this.size;
const start = (this.page - 1) * size;
return this.source.slice(start, start + size);
}),
});

View File

@ -9,7 +9,7 @@ export default Component.extend({
// Plan for a future with metadata (e.g., isSelected)
decoratedSource: computed('source.[]', function() {
return this.get('source').map(row => ({
return this.source.map(row => ({
model: row,
}));
}),

View File

@ -16,10 +16,10 @@ export default Component.extend({
classNameBindings: ['isActive:is-active', 'sortDescending:desc:asc'],
isActive: computed('currentProp', 'prop', function() {
return this.get('currentProp') === this.get('prop');
return this.currentProp === this.prop;
}),
shouldSortDescending: computed('sortDescending', 'isActive', function() {
return !this.get('isActive') || !this.get('sortDescending');
return !this.isActive || !this.sortDescending;
}),
});

View File

@ -27,8 +27,8 @@ export default Component.extend({
},
didReceiveAttrs() {
const dropdown = this.get('dropdown');
if (this.get('isOpen') && dropdown) {
const dropdown = this.dropdown;
if (this.isOpen && dropdown) {
run.scheduleOnce('afterRender', () => {
dropdown.actions.reposition();
});
@ -37,22 +37,22 @@ export default Component.extend({
actions: {
toggle({ key }) {
const newSelection = this.get('selection').slice();
const newSelection = this.selection.slice();
if (newSelection.includes(key)) {
newSelection.removeObject(key);
} else {
newSelection.addObject(key);
}
this.get('onSelect')(newSelection);
this.onSelect(newSelection);
},
openOnArrowDown(dropdown, e) {
this.capture(dropdown);
if (!this.get('isOpen') && e.keyCode === ARROW_DOWN) {
if (!this.isOpen && e.keyCode === ARROW_DOWN) {
dropdown.actions.open(e);
e.preventDefault();
} else if (this.get('isOpen') && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) {
} 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`);
@ -66,7 +66,7 @@ export default Component.extend({
traverseList(option, e) {
if (e.keyCode === ESC) {
// Close the dropdown
const dropdown = this.get('dropdown');
const dropdown = this.dropdown;
if (dropdown) {
dropdown.actions.close(e);
// Return focus to the trigger so tab works as expected

View File

@ -20,24 +20,24 @@ export default Component.extend({
// An instance of a StatsTracker. An alternative interface to resource
tracker: computed('trackedResource', 'type', function() {
const resource = this.get('trackedResource');
return this.get('statsTrackersRegistry').getTracker(resource);
const resource = this.trackedResource;
return this.statsTrackersRegistry.getTracker(resource);
}),
type: computed('resource', function() {
const resource = this.get('resource');
const resource = this.resource;
return resource && resource.constructor.modelName;
}),
trackedResource: computed('resource', 'type', function() {
// TaskStates use the allocation stats tracker
return this.get('type') === 'task-state'
return this.type === 'task-state'
? this.get('resource.allocation')
: this.get('resource');
: this.resource;
}),
metricLabel: computed('metric', function() {
const metric = this.get('metric');
const metric = this.metric;
const mappings = {
cpu: 'CPU',
memory: 'Memory',
@ -46,10 +46,10 @@ export default Component.extend({
}),
data: computed('resource', 'metric', 'type', function() {
if (!this.get('tracker')) return [];
if (!this.tracker) return [];
const metric = this.get('metric');
if (this.get('type') === 'task-state') {
const metric = this.metric;
if (this.type === 'task-state') {
// handle getting the right task out of the tracker
const task = this.get('tracker.tasks').findBy('task', this.get('resource.name'));
return task && task[metric];
@ -59,9 +59,9 @@ export default Component.extend({
}),
reservedAmount: computed('resource', 'metric', 'type', function() {
const metricProperty = this.get('metric') === 'cpu' ? 'reservedCPU' : 'reservedMemory';
const metricProperty = this.metric === 'cpu' ? 'reservedCPU' : 'reservedMemory';
if (this.get('type') === 'task-state') {
if (this.type === 'task-state') {
const task = this.get('tracker.tasks').findBy('task', this.get('resource.name'));
return task[metricProperty];
}
@ -70,7 +70,7 @@ export default Component.extend({
}),
chartClass: computed('metric', function() {
const metric = this.get('metric');
const metric = this.metric;
const mappings = {
cpu: 'is-info',
memory: 'is-danger',
@ -87,13 +87,13 @@ export default Component.extend({
}),
didReceiveAttrs() {
if (this.get('tracker')) {
this.get('poller').perform();
if (this.tracker) {
this.poller.perform();
}
},
willDestroy() {
this.get('poller').cancelAll();
this.poller.cancelAll();
this.get('tracker.signalPause').perform();
},
});

View File

@ -14,7 +14,7 @@ export default Component.extend({
}),
gotoRegion(region) {
this.get('router').transitionTo('jobs', {
this.router.transitionTo('jobs', {
queryParams: { region },
});
},

View File

@ -11,7 +11,7 @@ export default Component.extend({
// An allocation can also be provided directly
allocation: computed('allocationId', function() {
return this.get('store').findRecord('allocation', this.get('allocationId'));
return this.store.findRecord('allocation', this.allocationId);
}),
time: null,

View File

@ -20,18 +20,18 @@ export default Component.extend({
actions: {
setSearchTerm(e) {
this.set('_searchTerm', e.target.value);
run.debounce(this, updateSearch, this.get('debounce'));
run.debounce(this, updateSearch, this.debounce);
},
clear() {
this.set('_searchTerm', '');
run.debounce(this, updateSearch, this.get('debounce'));
run.debounce(this, updateSearch, this.debounce);
},
},
});
function updateSearch() {
const newTerm = this.get('_searchTerm');
const newTerm = this._searchTerm;
this.onChange(newTerm);
this.set('searchTerm', newTerm);
}

View File

@ -22,8 +22,8 @@ export default Component.extend({
// const targetURL = this.get('router').urlFor('servers.server', this.get('agent'));
// const currentURL = `${this.get('router.rootURL').slice(0, -1)}${this.get('router.currentURL')}`;
const router = this.get('router');
const targetURL = router.generate('servers.server', this.get('agent'));
const router = this.router;
const targetURL = router.generate('servers.server', this.agent);
const currentURL = `${router.get('rootURL').slice(0, -1)}${
router.get('currentURL').split('?')[0]
}`;
@ -33,7 +33,7 @@ export default Component.extend({
}),
click() {
const transition = () => this.get('router').transitionTo('servers.server', this.get('agent'));
const transition = () => this.router.transitionTo('servers.server', this.agent);
lazyClick([transition, event]);
},
});

View File

@ -24,7 +24,7 @@ export default LineChart.extend({
title: 'Stats Time Series Chart',
description: computed('data.[]', 'xProp', 'yProp', function() {
const { xProp, yProp, data } = this.getProperties('data', 'xProp', 'yProp');
const { xProp, yProp, data } = this;
const yRange = d3Array.extent(data, d => d[yProp]);
const xRange = d3Array.extent(data, d => d[xProp]);
const yFormatter = this.yFormat();
@ -35,9 +35,9 @@ export default LineChart.extend({
}),
xScale: computed('data.[]', 'xProp', 'timeseries', 'yAxisOffset', function() {
const xProp = this.get('xProp');
const scale = this.get('timeseries') ? d3Scale.scaleTime() : d3Scale.scaleLinear();
const data = this.get('data');
const xProp = this.xProp;
const scale = this.timeseries ? d3Scale.scaleTime() : d3Scale.scaleLinear();
const data = this.data;
const [low, high] = d3Array.extent(data, d => d[xProp]);
const minLow = moment(high)
@ -45,14 +45,14 @@ export default LineChart.extend({
.toDate();
const extent = data.length ? [Math.min(low, minLow), high] : [minLow, new Date()];
scale.rangeRound([10, this.get('yAxisOffset')]).domain(extent);
scale.rangeRound([10, this.yAxisOffset]).domain(extent);
return scale;
}),
yScale: computed('data.[]', 'yProp', 'xAxisOffset', function() {
const yProp = this.get('yProp');
const yValues = (this.get('data') || []).mapBy(yProp);
const yProp = this.yProp;
const yValues = (this.data || []).mapBy(yProp);
let [low, high] = [0, 1];
if (yValues.compact().length) {
@ -61,7 +61,7 @@ export default LineChart.extend({
return d3Scale
.scaleLinear()
.rangeRound([this.get('xAxisOffset'), 10])
.rangeRound([this.xAxisOffset, 10])
.domain([Math.min(0, low), Math.max(1, high)]);
}),
});

View File

@ -11,6 +11,6 @@ export default Component.extend({
onClick() {},
click(event) {
lazyClick([this.get('onClick'), event]);
lazyClick([this.onClick, event]);
},
});

View File

@ -26,7 +26,7 @@ export default Component.extend(WindowResizable, {
serverTimeout: 5000,
didReceiveAttrs() {
if (this.get('allocation') && this.get('task')) {
if (this.allocation && this.task) {
this.send('toggleStream');
}
},
@ -53,29 +53,29 @@ export default Component.extend(WindowResizable, {
const allocation = this.get('allocation.id');
const url = `/v1/client/fs/logs/${allocation}`;
return this.get('useServer') ? url : `//${address}${url}`;
return this.useServer ? url : `//${address}${url}`;
}),
logParams: computed('task', 'mode', function() {
return {
task: this.get('task'),
type: this.get('mode'),
task: this.task,
type: this.mode,
};
}),
logger: 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
const timing = this.get('useServer') ? this.get('serverTimeout') : this.get('clientTimeout');
const timing = this.useServer ? this.serverTimeout : this.clientTimeout;
return url =>
RSVP.race([this.get('token').authorizedRequest(url), timeout(timing)]).then(
RSVP.race([this.token.authorizedRequest(url), timeout(timing)]).then(
response => response,
error => {
if (this.get('useServer')) {
if (this.useServer) {
this.set('noConnection', true);
} else {
this.send('failoverToServer');
this.get('stream').perform();
this.stream.perform();
}
throw error;
}
@ -98,32 +98,32 @@ export default Component.extend(WindowResizable, {
}),
stream: task(function*() {
this.get('logger').on('tick', () => {
this.logger.on('tick', () => {
run.scheduleOnce('afterRender', () => {
const cliWindow = this.$('.cli-window');
cliWindow.scrollTop(cliWindow[0].scrollHeight);
});
});
yield this.get('logger').startStreaming();
this.get('logger').off('tick');
yield this.logger.startStreaming();
this.logger.off('tick');
}),
willDestroy() {
this.get('logger').stop();
this.logger.stop();
},
actions: {
setMode(mode) {
this.get('logger').stop();
this.logger.stop();
this.set('mode', mode);
this.get('stream').perform();
this.stream.perform();
},
toggleStream() {
if (this.get('logger.isStreaming')) {
this.get('logger').stop();
this.logger.stop();
} else {
this.get('stream').perform();
this.stream.perform();
}
},
failoverToServer() {

View File

@ -25,11 +25,11 @@ export default Component.extend({
stats: computed('task', 'task.isRunning', function() {
if (!this.get('task.isRunning')) return;
return this.get('statsTrackersRegistry').getTracker(this.get('task.allocation'));
return this.statsTrackersRegistry.getTracker(this.get('task.allocation'));
}),
taskStats: computed('task.name', 'stats.tasks.[]', function() {
if (!this.get('stats')) return;
if (!this.stats) return;
return this.get('stats.tasks').findBy('task', this.get('task.name'));
}),
@ -40,12 +40,12 @@ export default Component.extend({
onClick() {},
click(event) {
lazyClick([this.get('onClick'), event]);
lazyClick([this.onClick, event]);
},
fetchStats: task(function*() {
do {
if (this.get('stats')) {
if (this.stats) {
try {
yield this.get('stats.poll').perform();
this.set('statsError', false);
@ -55,16 +55,16 @@ export default Component.extend({
}
yield timeout(500);
} while (this.get('enablePolling'));
} while (this.enablePolling);
}).drop(),
didReceiveAttrs() {
const allocation = this.get('task.allocation');
if (allocation) {
this.get('fetchStats').perform();
this.fetchStats.perform();
} else {
this.get('fetchStats').cancelAll();
this.fetchStats.cancelAll();
}
},
});

View File

@ -25,7 +25,7 @@ export default Component.extend({
this.set('state', 'prompt');
},
confirm() {
RSVP.resolve(this.get('onConfirm')()).then(() => {
RSVP.resolve(this.onConfirm()).then(() => {
this.send('setToIdle');
});
},

View File

@ -19,39 +19,39 @@ export default Controller.extend({
error: null,
errorStr: computed('error', function() {
return this.get('error').toString();
return this.error.toString();
}),
errorCodes: computed('error', function() {
return codesForError(this.get('error'));
return codesForError(this.error);
}),
is403: computed('errorCodes.[]', function() {
return this.get('errorCodes').includes('403');
return this.errorCodes.includes('403');
}),
is404: computed('errorCodes.[]', function() {
return this.get('errorCodes').includes('404');
return this.errorCodes.includes('404');
}),
is500: computed('errorCodes.[]', function() {
return this.get('errorCodes').includes('500');
return this.errorCodes.includes('500');
}),
isNoLeader: computed('error', function() {
const error = this.get('error');
const error = this.error;
return error instanceof NoLeaderError;
}),
throwError: observer('error', function() {
if (this.get('config.isDev')) {
run.next(() => {
throw this.get('error');
throw this.error;
});
} else if (!Ember.testing) {
run.next(() => {
// eslint-disable-next-line
console.warn('UNRECOVERABLE ERROR:', this.get('error'));
console.warn('UNRECOVERABLE ERROR:', this.error);
});
}
}),

View File

@ -43,11 +43,11 @@ export default Controller.extend(Sortable, Searchable, {
selectionFlags: selection('qpFlags'),
optionsClass: computed('nodes.[]', function() {
const classes = Array.from(new Set(this.get('nodes').mapBy('nodeClass'))).compact();
const classes = Array.from(new Set(this.nodes.mapBy('nodeClass'))).compact();
// Remove any invalid node classes from the query param/selection
scheduleOnce('actions', () => {
this.set('qpClass', serialize(intersection(classes, this.get('selectionClass'))));
this.set('qpClass', serialize(intersection(classes, this.selectionClass)));
});
return classes.sort().map(dc => ({ key: dc, label: dc }));
@ -60,13 +60,13 @@ export default Controller.extend(Sortable, Searchable, {
]),
optionsDatacenter: computed('nodes.[]', function() {
const datacenters = Array.from(new Set(this.get('nodes').mapBy('datacenter'))).compact();
const datacenters = Array.from(new Set(this.nodes.mapBy('datacenter'))).compact();
// Remove any invalid datacenters from the query param/selection
scheduleOnce('actions', () => {
this.set(
'qpDatacenter',
serialize(intersection(datacenters, this.get('selectionDatacenter')))
serialize(intersection(datacenters, this.selectionDatacenter))
);
});
@ -90,17 +90,12 @@ export default Controller.extend(Sortable, Searchable, {
selectionStatus: statuses,
selectionDatacenter: datacenters,
selectionFlags: flags,
} = this.getProperties(
'selectionClass',
'selectionStatus',
'selectionDatacenter',
'selectionFlags'
);
} = this;
const onlyIneligible = flags.includes('ineligible');
const onlyDraining = flags.includes('draining');
return this.get('nodes').filter(node => {
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;

View File

@ -62,7 +62,7 @@ export default Controller.extend(Sortable, Searchable, {
optionsDatacenter: computed('visibleJobs.[]', function() {
const flatten = (acc, val) => acc.concat(val);
const allDatacenters = new Set(
this.get('visibleJobs')
this.visibleJobs
.mapBy('datacenters')
.reduce(flatten, [])
);
@ -72,7 +72,7 @@ export default Controller.extend(Sortable, Searchable, {
scheduleOnce('actions', () => {
this.set(
'qpDatacenter',
serialize(intersection(availableDatacenters, this.get('selectionDatacenter')))
serialize(intersection(availableDatacenters, this.selectionDatacenter))
);
});
@ -85,7 +85,7 @@ export default Controller.extend(Sortable, Searchable, {
const hasPrefix = /.[-._]/;
// Collect and count all the prefixes
const allNames = this.get('visibleJobs').mapBy('name');
const allNames = this.visibleJobs.mapBy('name');
const nameHistogram = allNames.reduce((hist, name) => {
if (hasPrefix.test(name)) {
const prefix = name.match(/(.+?)[-.]/)[1];
@ -106,7 +106,7 @@ export default Controller.extend(Sortable, Searchable, {
// Remove any invalid prefixes from the query param/selection
const availablePrefixes = prefixes.mapBy('prefix');
scheduleOnce('actions', () => {
this.set('qpPrefix', serialize(intersection(availablePrefixes, this.get('selectionPrefix'))));
this.set('qpPrefix', serialize(intersection(availablePrefixes, this.selectionPrefix)));
});
// Sort, format, and include the count in the label
@ -126,7 +126,7 @@ export default Controller.extend(Sortable, Searchable, {
const hasNamespaces = this.get('system.namespaces.length');
const activeNamespace = this.get('system.activeNamespace.id') || 'default';
return this.get('model')
return this.model
.compact()
.filter(job => !hasNamespaces || job.get('namespace.id') === activeNamespace)
.filter(job => !job.get('parent.content'));
@ -144,16 +144,11 @@ export default Controller.extend(Sortable, Searchable, {
selectionStatus: statuses,
selectionDatacenter: datacenters,
selectionPrefix: prefixes,
} = this.getProperties(
'selectionType',
'selectionStatus',
'selectionDatacenter',
'selectionPrefix'
);
} = 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.get('visibleJobs').filter(job => {
return this.visibleJobs.filter(job => {
if (types.length && !types.includes(job.get('displayType'))) {
return false;
}

View File

@ -9,7 +9,7 @@ export default Controller.extend(WithNamespaceResetting, {
isEditing: false,
edit() {
this.get('job').set('_newDefinition', JSON.stringify(this.get('definition'), null, 2));
this.job.set('_newDefinition', JSON.stringify(this.definition, null, 2));
this.set('isEditing', true);
},

View File

@ -15,12 +15,12 @@ export default Controller.extend({
tokenRecord: null,
resetStore() {
this.get('store').unloadAll();
this.store.unloadAll();
},
actions: {
clearTokenProperties() {
this.get('token').setProperties({
this.token.setProperties({
secret: undefined,
});
this.setProperties({
@ -32,7 +32,7 @@ export default Controller.extend({
},
verifyToken() {
const { secret } = this.getProperties('secret', 'accessor');
const { secret } = this;
const TokenAdapter = getOwner(this).lookup('adapter:token');
this.set('token.secret', secret);
@ -44,11 +44,11 @@ export default Controller.extend({
// Clear out all data to ensure only data the new token is privileged to
// see is shown
this.get('system').reset();
this.system.reset();
this.resetStore();
// Immediately refetch the token now that the store is empty
const newToken = this.get('store').findRecord('token', tokenId);
const newToken = this.store.findRecord('token', tokenId);
this.setProperties({
tokenIsValid: true,

View File

@ -1,7 +1,8 @@
import Helper from '@ember/component/helper';
import { pluralize } from 'ember-inflector';
export function pluralize([term, count]) {
return count === 1 ? term : term.pluralize();
export function pluralizeHelper([term, count]) {
return count === 1 ? term : pluralize(term);
}
export default Helper.helper(pluralize);
export default Helper.helper(pluralizeHelper);

View File

@ -41,13 +41,13 @@ export default Mixin.create({
// preferable to generalize this rather than risking it being
// forgotten on a single page.
resetPagination() {
if (this.get('currentPage') != null) {
if (this.currentPage != null) {
this.set('currentPage', 1);
}
},
fuse: computed('listToSearch.[]', 'fuzzySearchProps.[]', function() {
return new Fuse(this.get('listToSearch'), {
return new Fuse(this.listToSearch, {
shouldSort: true,
threshold: 0.4,
location: 0,
@ -56,7 +56,7 @@ export default Mixin.create({
matchAllTokens: true,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: this.get('fuzzySearchProps') || [],
keys: this.fuzzySearchProps || [],
getFn(item, key) {
return get(item, key);
},
@ -73,31 +73,31 @@ export default Mixin.create({
'fuzzySearchProps.[]',
'regexSearchProps.[]',
function() {
const searchTerm = this.get('searchTerm').trim();
const searchTerm = this.searchTerm.trim();
if (!searchTerm || !searchTerm.length) {
return this.get('listToSearch');
return this.listToSearch;
}
const results = [];
if (this.get('exactMatchEnabled')) {
if (this.exactMatchEnabled) {
results.push(
...exactMatchSearch(
searchTerm,
this.get('listToSearch'),
this.get('exactMatchSearchProps')
this.listToSearch,
this.exactMatchSearchProps
)
);
}
if (this.get('fuzzySearchEnabled')) {
results.push(...this.get('fuse').search(searchTerm));
if (this.fuzzySearchEnabled) {
results.push(...this.fuse.search(searchTerm));
}
if (this.get('regexEnabled')) {
if (this.regexEnabled) {
results.push(
...regexSearch(searchTerm, this.get('listToSearch'), this.get('regexSearchProps'))
...regexSearch(searchTerm, this.listToSearch, this.regexSearchProps)
);
}

View File

@ -21,10 +21,10 @@ export default Mixin.create({
listToSort: computed(() => []),
listSorted: computed('listToSort.[]', 'sortProperty', 'sortDescending', function() {
const sorted = this.get('listToSort')
const sorted = this.listToSort
.compact()
.sortBy(this.get('sortProperty'));
if (this.get('sortDescending')) {
.sortBy(this.sortProperty);
if (this.sortDescending) {
return sorted.reverse();
}
return sorted;

View File

@ -10,12 +10,12 @@ export default Mixin.create({
setupWindowResize: function() {
run.scheduleOnce('afterRender', this, () => {
this.set('_windowResizeHandler', this.get('windowResizeHandler').bind(this));
$(window).on('resize', this.get('_windowResizeHandler'));
this.set('_windowResizeHandler', this.windowResizeHandler.bind(this));
$(window).on('resize', this._windowResizeHandler);
});
}.on('didInsertElement'),
removeWindowResize: function() {
$(window).off('resize', this.get('_windowResizeHandler'));
$(window).off('resize', this._windowResizeHandler);
}.on('willDestroyElement'),
});

View File

@ -9,14 +9,14 @@ export default Mixin.create({
setupDocumentVisibility: function() {
if (!Ember.testing) {
this.set('_visibilityHandler', this.get('visibilityHandler').bind(this));
document.addEventListener('visibilitychange', this.get('_visibilityHandler'));
this.set('_visibilityHandler', this.visibilityHandler.bind(this));
document.addEventListener('visibilitychange', this._visibilityHandler);
}
}.on('init'),
removeDocumentVisibility: function() {
if (!Ember.testing) {
document.removeEventListener('visibilitychange', this.get('_visibilityHandler'));
document.removeEventListener('visibilitychange', this._visibilityHandler);
}
}.on('willDestroy'),
});

View File

@ -2,7 +2,7 @@ import Mixin from '@ember/object/mixin';
export default Mixin.create({
setupController(controller) {
if (this.get('isForbidden')) {
if (this.isForbidden) {
this.set('isForbidden', undefined);
controller.set('isForbidden', true);
}

View File

@ -9,14 +9,14 @@ export default Mixin.create({
setupDocumentVisibility: function() {
if (!Ember.testing) {
this.set('_visibilityHandler', this.get('visibilityHandler').bind(this));
document.addEventListener('visibilitychange', this.get('_visibilityHandler'));
this.set('_visibilityHandler', this.visibilityHandler.bind(this));
document.addEventListener('visibilitychange', this._visibilityHandler);
}
}.on('activate'),
removeDocumentVisibility: function() {
if (!Ember.testing) {
document.removeEventListener('visibilitychange', this.get('_visibilityHandler'));
document.removeEventListener('visibilitychange', this._visibilityHandler);
}
}.on('deactivate'),
});

View File

@ -7,7 +7,7 @@ export default Mixin.create(WithVisibilityDetection, {
watchers: computed(() => []),
cancelAllWatchers() {
this.get('watchers').forEach(watcher => {
this.watchers.forEach(watcher => {
assert('Watchers must be Ember Concurrency Tasks.', !!watcher.cancelAll);
watcher.cancelAll();
});

View File

@ -16,11 +16,11 @@ export default Model.extend({
region: attr('string'),
rpcAddr: computed('address', 'port', function() {
const { address, rpcPort } = this.getProperties('address', 'rpcPort');
const { address, rpcPort } = this;
return address && rpcPort && `${address}:${rpcPort}`;
}),
isLeader: computed('system.leader.rpcAddr', function() {
return this.get('system.leader.rpcAddr') === this.get('rpcAddr');
return this.get('system.leader.rpcAddr') === this.rpcAddr;
}),
});

View File

@ -36,7 +36,7 @@ export default Model.extend({
clientStatus: attr('string'),
desiredStatus: attr('string'),
statusIndex: computed('clientStatus', function() {
return STATUS_ORDER[this.get('clientStatus')] || 100;
return STATUS_ORDER[this.clientStatus] || 100;
}),
isRunning: equal('clientStatus', 'running'),
@ -57,12 +57,12 @@ export default Model.extend({
lost: 'is-light',
};
return classMap[this.get('clientStatus')] || 'is-dark';
return classMap[this.clientStatus] || 'is-dark';
}),
taskGroup: computed('taskGroupName', 'job.taskGroups.[]', function() {
const taskGroups = this.get('job.taskGroups');
return taskGroups && taskGroups.findBy('name', this.get('taskGroupName'));
return taskGroups && taskGroups.findBy('name', this.taskGroupName);
}),
unhealthyDrivers: computed('taskGroup.drivers.[]', 'node.unhealthyDriverNames.[]', function() {
@ -80,19 +80,17 @@ export default Model.extend({
rescheduleEvents: fragmentArray('reschedule-event'),
hasRescheduleEvents: computed('rescheduleEvents.length', 'nextAllocation', function() {
return this.get('rescheduleEvents.length') > 0 || this.get('nextAllocation');
return this.get('rescheduleEvents.length') > 0 || this.nextAllocation;
}),
hasStoppedRescheduling: computed(
'nextAllocation',
'clientStatus',
'followUpEvaluation',
'followUpEvaluation.content',
function() {
return (
!this.get('nextAllocation.content') &&
!this.get('followUpEvaluation.content') &&
this.get('clientStatus') === 'failed'
);
return !this.get('nextAllocation.content') &&
!this.get('followUpEvaluation.content') &&
this.clientStatus === 'failed';
}
),
});

View File

@ -18,12 +18,10 @@ export default Model.extend({
// 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.get('status') === 'running' &&
this.get('taskGroupSummaries')
.toArray()
.some(summary => summary.get('requiresPromotion') && !summary.get('promoted'))
);
return this.status === 'running' &&
this.taskGroupSummaries
.toArray()
.some(summary => summary.get('requiresPromotion') && !summary.get('promoted'));
}),
status: attr('string'),
@ -35,7 +33,7 @@ export default Model.extend({
allocations: hasMany('allocations'),
version: computed('versionNumber', 'job.versions.content.@each.number', function() {
return (this.get('job.versions') || []).findBy('number', this.get('versionNumber'));
return (this.get('job.versions') || []).findBy('number', this.versionNumber);
}),
// Dependent keys can only go one level past an @each so an alias is needed
@ -57,11 +55,11 @@ export default Model.extend({
cancelled: 'is-cancelled',
};
return classMap[this.get('status')] || 'is-dark';
return classMap[this.status] || 'is-dark';
}),
promote() {
assert('A deployment needs to requirePromotion to be promoted', this.get('requiresPromotion'));
assert('A deployment needs to requirePromotion to be promoted', this.requiresPromotion);
return this.store.adapterFor('deployment').promote(this);
},
});

View File

@ -32,7 +32,7 @@ export default Model.extend({
parameterizedDetails: attr(),
hasChildren: computed('periodic', 'parameterized', 'dispatched', function() {
return this.get('periodic') || (this.get('parameterized') && !this.get('dispatched'));
return this.periodic || (this.parameterized && !this.dispatched);
}),
parent: belongsTo('job', { inverse: 'children' }),
@ -40,19 +40,19 @@ export default Model.extend({
// The parent job name is prepended to child launch job names
trimmedName: computed('name', 'parent', function() {
return this.get('parent.content') ? this.get('name').replace(/.+?\//, '') : this.get('name');
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() {
if (this.get('periodic')) {
if (this.periodic) {
return 'periodic';
} else if (this.get('parameterized')) {
} else if (this.parameterized) {
return 'parameterized';
}
return this.get('type');
return this.type;
}),
// A composite of type and other job attributes to determine
@ -64,20 +64,20 @@ export default Model.extend({
'parent.periodic',
'parent.parameterized',
function() {
const type = this.get('type');
const type = this.type;
if (this.get('parent.periodic')) {
return 'periodic-child';
} else if (this.get('parent.parameterized')) {
return 'parameterized-child';
} else if (this.get('periodic')) {
} else if (this.periodic) {
return 'periodic';
} else if (this.get('parameterized')) {
} 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.get('type');
return this.type;
}
// A fail-safe in the event the API introduces a new type.
@ -122,7 +122,7 @@ export default Model.extend({
namespace: belongsTo('namespace'),
drivers: computed('taskGroups.@each.drivers', function() {
return this.get('taskGroups')
return this.taskGroups
.mapBy('drivers')
.reduce((all, drivers) => {
all.push(...drivers);
@ -134,7 +134,7 @@ export default Model.extend({
// 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() {
return this.get('allocations')
return this.allocations
.mapBy('unhealthyDrivers')
.reduce((all, drivers) => {
all.push(...drivers);
@ -144,7 +144,7 @@ export default Model.extend({
}),
hasBlockedEvaluation: computed('evaluations.@each.isBlocked', function() {
return this.get('evaluations')
return this.evaluations
.toArray()
.some(evaluation => evaluation.get('isBlocked'));
}),
@ -152,7 +152,7 @@ export default Model.extend({
hasPlacementFailures: and('latestFailureEvaluation', 'hasBlockedEvaluation'),
latestEvaluation: computed('evaluations.@each.modifyIndex', 'evaluations.isPending', function() {
const evaluations = this.get('evaluations');
const evaluations = this.evaluations;
if (!evaluations || evaluations.get('isPending')) {
return null;
}
@ -163,7 +163,7 @@ export default Model.extend({
'evaluations.@each.modifyIndex',
'evaluations.isPending',
function() {
const evaluations = this.get('evaluations');
const evaluations = this.evaluations;
if (!evaluations || evaluations.get('isPending')) {
return null;
}
@ -180,7 +180,7 @@ export default Model.extend({
latestDeployment: belongsTo('deployment', { inverse: 'jobForLatest' }),
runningDeployment: computed('latestDeployment', 'latestDeployment.isRunning', function() {
const latest = this.get('latestDeployment');
const latest = this.latestDeployment;
if (latest.get('isRunning')) return latest;
}),
@ -197,22 +197,22 @@ export default Model.extend({
},
plan() {
assert('A job must be parsed before planned', this.get('_newDefinitionJSON'));
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.get('_newDefinitionJSON'));
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.get('_newDefinitionJSON'));
assert('A job must be parsed before updated', this._newDefinitionJSON);
return this.store.adapterFor('job').update(this);
},
parse() {
const definition = this.get('_newDefinition');
const definition = this._newDefinition;
let promise;
try {
@ -221,7 +221,7 @@ export default Model.extend({
this.set('_newDefinitionJSON', json);
// You can't set the ID of a record that already exists
if (this.get('isNew')) {
if (this.isNew) {
this.setIdByPayload(json);
}
@ -231,7 +231,7 @@ export default Model.extend({
// in anyway, the parse endpoint will throw an error.
promise = this.store
.adapterFor('job')
.parse(this.get('_newDefinition'))
.parse(this._newDefinition)
.then(response => {
this.set('_newDefinitionJSON', response);
this.setIdByPayload(response);
@ -255,7 +255,7 @@ export default Model.extend({
},
resetId() {
this.set('id', JSON.stringify([this.get('plainId'), this.get('namespace.name') || 'default']));
this.set('id', JSON.stringify([this.plainId, this.get('namespace.name') || 'default']));
},
statusClass: computed('status', function() {
@ -265,13 +265,13 @@ export default Model.extend({
dead: 'is-light',
};
return classMap[this.get('status')] || 'is-dark';
return classMap[this.status] || 'is-dark';
}),
payload: attr('string'),
decodedPayload: computed('payload', function() {
// Lazily decode the base64 encoded payload
return window.atob(this.get('payload') || '');
return window.atob(this.payload || '');
}),
// An arbitrary HCL or JSON string that is used by the serializer to plan

View File

@ -9,7 +9,7 @@ export default Fragment.extend({
attributes: attr(),
attributesStructured: computed('attributes', function() {
const original = this.get('attributes');
const original = this.attributes;
if (!original) {
return;
@ -30,6 +30,6 @@ export default Fragment.extend({
//
// ex: nodeAttrs.get('driver.docker')
// [ "1", { version: "17.05.0-ce", volumes: { enabled: "1" } } ]
return get(this.get('attributesStructured'), key);
return get(this.attributesStructured, key);
},
});

View File

@ -11,7 +11,7 @@ export default Fragment.extend({
attributesShort: computed('name', 'attributes.attributesStructured', function() {
const attributes = this.get('attributes.attributesStructured');
return get(attributes, `driver.${this.get('name')}`);
return get(attributes, `driver.${this.name}`);
}),
name: attr('string'),
@ -21,6 +21,6 @@ export default Fragment.extend({
updateTime: attr('date'),
healthClass: computed('healthy', function() {
return this.get('healthy') ? 'running' : 'failed';
return this.healthy ? 'running' : 'failed';
}),
});

View File

@ -31,15 +31,15 @@ export default Model.extend({
isEligible: equal('schedulingEligibility', 'eligible'),
address: computed('httpAddr', function() {
return ipParts(this.get('httpAddr')).address;
return ipParts(this.httpAddr).address;
}),
port: computed('httpAddr', function() {
return ipParts(this.get('httpAddr')).port;
return ipParts(this.httpAddr).port;
}),
isPartial: computed('httpAddr', function() {
return this.get('httpAddr') == null;
return this.httpAddr == null;
}),
allocations: hasMany('allocations', { inverse: 'node' }),
@ -48,20 +48,20 @@ export default Model.extend({
events: fragmentArray('node-event'),
detectedDrivers: computed('drivers.@each.detected', function() {
return this.get('drivers').filterBy('detected');
return this.drivers.filterBy('detected');
}),
unhealthyDrivers: computed('detectedDrivers.@each.healthy', function() {
return this.get('detectedDrivers').filterBy('healthy', false);
return this.detectedDrivers.filterBy('healthy', false);
}),
unhealthyDriverNames: computed('unhealthyDrivers.@each.name', function() {
return this.get('unhealthyDrivers').mapBy('name');
return this.unhealthyDrivers.mapBy('name');
}),
// A status attribute that includes states not included in node status.
// Useful for coloring and sorting nodes
compositeStatus: computed('status', 'isEligible', function() {
return this.get('isEligible') ? this.get('status') : 'ineligible';
return this.isEligible ? this.status : 'ineligible';
}),
});

View File

@ -15,13 +15,13 @@ export default Fragment.extend({
tasks: fragmentArray('task'),
drivers: computed('tasks.@each.driver', function() {
return this.get('tasks')
return this.tasks
.mapBy('driver')
.uniq();
}),
allocations: computed('job.allocations.@each.taskGroup', function() {
return maybe(this.get('job.allocations')).filterBy('taskGroupName', this.get('name'));
return maybe(this.get('job.allocations')).filterBy('taskGroupName', this.name);
}),
reservedCPU: sumAggregation('tasks', 'reservedCPU'),
@ -32,7 +32,7 @@ export default Fragment.extend({
placementFailures: computed('job.latestFailureEvaluation.failedTGAllocs.[]', function() {
const placementFailures = this.get('job.latestFailureEvaluation.failedTGAllocs');
return placementFailures && placementFailures.findBy('name', this.get('name'));
return placementFailures && placementFailures.findBy('name', this.name);
}),
queuedOrStartingAllocs: computed('summary.{queuedAllocs,startingAllocs}', function() {
@ -40,6 +40,6 @@ export default Fragment.extend({
}),
summary: computed('job.taskGroupSummaries.[]', function() {
return maybe(this.get('job.taskGroupSummaries')).findBy('name', this.get('name'));
return maybe(this.get('job.taskGroupSummaries')).findBy('name', this.name);
}),
});

View File

@ -18,7 +18,7 @@ export default Fragment.extend({
task: computed('allocation.taskGroup.tasks.[]', function() {
const tasks = this.get('allocation.taskGroup.tasks');
return tasks && tasks.findBy('name', this.get('name'));
return tasks && tasks.findBy('name', this.name);
}),
driver: alias('task.driver'),
@ -41,6 +41,6 @@ export default Fragment.extend({
failed: 'is-error',
};
return classMap[this.get('state')] || 'is-dark';
return classMap[this.state] || 'is-dark';
}),
});

View File

@ -9,7 +9,7 @@ import { jobCrumbs } from 'nomad-ui/utils/breadcrumb-utils';
export default Route.extend(WithWatchers, {
startWatchers(controller, model) {
if (model) {
controller.set('watcher', this.get('watch').perform(model));
controller.set('watcher', this.watch.perform(model));
}
},

View File

@ -35,8 +35,8 @@ export default Route.extend({
(queryParam && queryParam !== currentRegion) ||
(!queryParam && currentRegion !== defaultRegion)
) {
this.get('system').reset();
this.get('store').unloadAll();
this.system.reset();
this.store.unloadAll();
}
this.set('system.activeRegion', queryParam || defaultRegion);

View File

@ -21,8 +21,8 @@ export default Route.extend(WithForbiddenState, {
model() {
return RSVP.hash({
nodes: this.get('store').findAll('node'),
agents: this.get('store').findAll('agent'),
nodes: this.store.findAll('node'),
agents: this.store.findAll('agent'),
}).catch(notifyForbidden(this));
},
});

View File

@ -31,8 +31,8 @@ export default Route.extend(WithWatchers, {
startWatchers(controller, model) {
if (model) {
controller.set('watchModel', this.get('watch').perform(model));
controller.set('watchAllocations', this.get('watchAllocations').perform(model));
controller.set('watchModel', this.watch.perform(model));
controller.set('watchAllocations', this.watchAllocations.perform(model));
}
},

View File

@ -5,7 +5,7 @@ import WithWatchers from 'nomad-ui/mixins/with-watchers';
export default Route.extend(WithWatchers, {
startWatchers(controller) {
controller.set('watcher', this.get('watch').perform());
controller.set('watcher', this.watch.perform());
},
watch: watchAll('node'),

View File

@ -6,7 +6,7 @@ export default Route.extend({
emberFreestyle: service(),
beforeModel() {
let emberFreestyle = this.get('emberFreestyle');
let emberFreestyle = this.emberFreestyle;
return emberFreestyle.ensureHljs().then(() => {
return RSVP.all([

View File

@ -30,7 +30,7 @@ export default Route.extend(WithForbiddenState, {
},
model() {
return this.get('store')
return this.store
.findAll('job', { reload: true })
.catch(notifyForbidden(this));
},

View File

@ -5,7 +5,7 @@ import WithWatchers from 'nomad-ui/mixins/with-watchers';
export default Route.extend(WithWatchers, {
startWatchers(controller) {
controller.set('modelWatch', this.get('watch').perform());
controller.set('modelWatch', this.watch.perform());
},
watch: watchAll('job'),

View File

@ -18,7 +18,7 @@ export default Route.extend({
const namespace = transition.queryParams.namespace || this.get('system.activeNamespace.id');
const name = params.job_name;
const fullId = JSON.stringify([name, namespace || 'default']);
return this.get('store')
return this.store
.findRecord('job', fullId, { reload: true })
.then(job => {
return RSVP.all([job.get('allocations'), job.get('evaluations')]).then(() => job);

View File

@ -11,7 +11,7 @@ export default Route.extend(WithWatchers, {
startWatchers(controller, model) {
if (model) {
controller.set('watchAllocations', this.get('watchAllocations').perform(model));
controller.set('watchAllocations', this.watchAllocations.perform(model));
}
},

View File

@ -13,7 +13,7 @@ export default Route.extend({
resetController(controller, isExiting) {
if (isExiting) {
const job = controller.get('job');
const job = controller.job;
job.rollbackAttributes();
job.resetId();
controller.set('isEditing', false);

View File

@ -12,8 +12,8 @@ export default Route.extend(WithWatchers, {
startWatchers(controller, model) {
if (model) {
controller.set('watchDeployments', this.get('watchDeployments').perform(model));
controller.set('watchVersions', this.get('watchVersions').perform(model));
controller.set('watchDeployments', this.watchDeployments.perform(model));
controller.set('watchVersions', this.watchVersions.perform(model));
}
},

View File

@ -11,7 +11,7 @@ export default Route.extend(WithWatchers, {
startWatchers(controller, model) {
if (model) {
controller.set('watchEvaluations', this.get('watchEvaluations').perform(model));
controller.set('watchEvaluations', this.watchEvaluations.perform(model));
}
},

View File

@ -9,13 +9,13 @@ export default Route.extend(WithWatchers, {
return;
}
controller.set('watchers', {
model: this.get('watch').perform(model),
summary: this.get('watchSummary').perform(model.get('summary')),
allocations: this.get('watchAllocations').perform(model),
evaluations: this.get('watchEvaluations').perform(model),
model: this.watch.perform(model),
summary: this.watchSummary.perform(model.get('summary')),
allocations: this.watchAllocations.perform(model),
evaluations: this.watchEvaluations.perform(model),
latestDeployment:
model.get('supportsDeployments') && this.get('watchLatestDeployment').perform(model),
list: model.get('hasChildren') && this.get('watchAll').perform(),
model.get('supportsDeployments') && this.watchLatestDeployment.perform(model),
list: model.get('hasChildren') && this.watchAll.perform(),
});
},

View File

@ -55,9 +55,9 @@ export default Route.extend(WithWatchers, {
if (model) {
const job = model.get('job');
controller.set('watchers', {
job: this.get('watchJob').perform(job),
summary: this.get('watchSummary').perform(job.get('summary')),
allocations: this.get('watchAllocations').perform(job),
job: this.watchJob.perform(job),
summary: this.watchSummary.perform(job.get('summary')),
allocations: this.watchAllocations.perform(job),
});
}
},

View File

@ -11,7 +11,7 @@ export default Route.extend(WithWatchers, {
startWatchers(controller, model) {
if (model) {
controller.set('watcher', this.get('watchVersions').perform(model));
controller.set('watcher', this.watchVersions.perform(model));
}
},

View File

@ -13,14 +13,14 @@ export default Route.extend({
],
model() {
return this.get('store').createRecord('job', {
return this.store.createRecord('job', {
namespace: this.get('system.activeNamespace'),
});
},
resetController(controller, isExiting) {
if (isExiting) {
controller.get('model').deleteRecord();
controller.model.deleteRecord();
}
},
});

View File

@ -21,8 +21,8 @@ export default Route.extend(WithForbiddenState, {
model() {
return RSVP.hash({
nodes: this.get('store').findAll('node'),
agents: this.get('store').findAll('agent'),
nodes: this.store.findAll('node'),
agents: this.store.findAll('agent'),
}).catch(notifyForbidden(this));
},
});

View File

@ -1,7 +1,8 @@
import { copy } from '@ember/object/internals';
import { copy } from 'ember-copy';
import { get } from '@ember/object';
import { makeArray } from '@ember/array';
import JSONSerializer from 'ember-data/serializers/json';
import { pluralize, singularize } from 'ember-inflector';
import removeRecord from '../utils/remove-record';
export default JSONSerializer.extend({
@ -12,11 +13,10 @@ export default JSONSerializer.extend({
},
keyForRelationship(attr, relationshipType) {
const key = `${attr
.singularize()
const key = `${singularize(attr)
.camelize()
.capitalize()}ID`;
return relationshipType === 'hasMany' ? key.pluralize() : key;
return relationshipType === 'hasMany' ? pluralize(key) : key;
},
// Modeled after the pushPayload for ember-data/serializers/rest
@ -68,6 +68,6 @@ export default JSONSerializer.extend({
},
modelNameFromPayloadKey(key) {
return key.dasherize().singularize();
return singularize(key.dasherize());
},
});

View File

@ -1,4 +1,4 @@
import { copy } from '@ember/object/internals';
import { copy } from 'ember-copy';
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({

View File

@ -1,4 +1,4 @@
import { copy } from '@ember/object/internals';
import { copy } from 'ember-copy';
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({

View File

@ -22,14 +22,14 @@ export default Service.extend({
// Routes can reset the breadcrumb trail to start anew even
// if the route is deeply nested.
if (route.get('resetBreadcrumbs')) {
if (route.resetBreadcrumbs) {
crumbs = [];
}
// Breadcrumbs are either an array of static crumbs
// or a function that returns breadcrumbs given the current
// model for the route's controller.
let breadcrumbs = route.get('breadcrumbs') || [];
let breadcrumbs = route.breadcrumbs || [];
if (typeof breadcrumbs === 'function') {
breadcrumbs = breadcrumbs(route.get('controller.model')) || [];
}

View File

@ -45,7 +45,7 @@ export default Service.extend({
}
const tracker = Constructor.create({
fetch: url => this.get('token').authorizedRequest(url),
fetch: url => this.token.authorizedRequest(url),
[resourceProp]: resource,
});

View File

@ -10,7 +10,7 @@ export default Service.extend({
store: service(),
leader: computed('activeRegion', function() {
const token = this.get('token');
const token = this.token;
return PromiseObject.create({
promise: token
@ -26,7 +26,7 @@ export default Service.extend({
}),
defaultRegion: computed(function() {
const token = this.get('token');
const token = this.token;
return PromiseObject.create({
promise: token
.authorizedRawRequest(`/${namespace}/agent/members`)
@ -38,7 +38,7 @@ export default Service.extend({
}),
regions: computed(function() {
const token = this.get('token');
const token = this.token;
return PromiseArray.create({
promise: token.authorizedRawRequest(`/${namespace}/regions`).then(jsonWithDefault([])),
@ -47,7 +47,7 @@ export default Service.extend({
activeRegion: computed('regions.[]', {
get() {
const regions = this.get('regions');
const regions = this.regions;
const region = window.localStorage.nomadActiveRegion;
if (regions.includes(region)) {
@ -78,30 +78,28 @@ export default Service.extend({
'defaultRegion.region',
'shouldShowRegions',
function() {
return (
this.get('shouldShowRegions') &&
this.get('activeRegion') !== this.get('defaultRegion.region')
);
return this.shouldShowRegions &&
this.activeRegion !== this.get('defaultRegion.region');
}
),
namespaces: computed('activeRegion', function() {
return PromiseArray.create({
promise: this.get('store')
promise: this.store
.findAll('namespace')
.then(namespaces => namespaces.compact()),
});
}),
shouldShowNamespaces: computed('namespaces.[]', function() {
const namespaces = this.get('namespaces').toArray();
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.get('namespaces').findBy('id', namespaceId);
const namespace = this.namespaces.findBy('id', namespaceId);
if (namespace) {
return namespace;
@ -110,14 +108,14 @@ export default Service.extend({
// If the namespace in localStorage is no longer in the cluster, it needs to
// be cleared from localStorage
window.localStorage.removeItem('nomadActiveNamespace');
return this.get('namespaces').findBy('id', 'default');
return this.namespaces.findBy('id', 'default');
},
set(key, value) {
if (value == null) {
window.localStorage.removeItem('nomadActiveNamespace');
} else if (typeof value === 'string') {
window.localStorage.nomadActiveNamespace = value;
return this.get('namespaces').findBy('id', value);
return this.namespaces.findBy('id', value);
} else {
window.localStorage.nomadActiveNamespace = value.get('name');
return value;

View File

@ -29,7 +29,7 @@ export default Service.extend({
// param since the region cannot be known at this point.
authorizedRawRequest(url, options = { credentials: 'include' }) {
const headers = {};
const token = this.get('secret');
const token = this.secret;
if (token) {
headers['X-Nomad-Token'] = token;

View File

@ -1,5 +1,5 @@
import { readOnly } from '@ember/object/computed';
import { copy } from '@ember/object/internals';
import { copy } from 'ember-copy';
import Service from '@ember/service';
let list = {};

View File

@ -65,7 +65,7 @@
<td data-test-task-address-is-dynamic>{{if row.model.isDynamic "Yes" "No"}}</td>
<td data-test-task-address-name>{{row.model.name}}</td>
<td data-test-task-address-address>
<a href="http://{{network.ip}}:{{row.model.port}}" target="_blank">
<a href="http://{{network.ip}}:{{row.model.port}}" target="_blank" rel="noopener noreferrer">
{{network.ip}}:{{row.model.port}}
</a>
</td>

View File

@ -1,18 +1,18 @@
{{#freestyle-usage "accordion" title="Accordion"}}
{{#list-accordion source=products key="name" as |a|}}
{{#a.head buttonLabel="details"}}
{{#list-accordion source=products key="name" as |ac|}}
{{#ac.head buttonLabel="details"}}
<div class="columns inline-definitions">
<div class="column is-1">{{a.item.name}}</div>
<div class="column is-1">{{ac.item.name}}</div>
<div class="column is-1">
<span class="bumper-left badge is-light">{{a.item.lang}}</span>
<span class="bumper-left badge is-light">{{ac.item.lang}}</span>
</div>
</div>
{{/a.head}}
{{#a.body}}
<h1 class="title is-4">{{a.item.name}}</h1>
<p>{{a.item.desc}}</p>
<p><a href="{{a.item.link}}">Learn more...</a></p>
{{/a.body}}
{{/ac.head}}
{{#ac.body}}
<h1 class="title is-4">{{ac.item.name}}</h1>
<p>{{ac.item.desc}}</p>
<p><a href="{{ac.item.link}}">Learn more...</a></p>
{{/ac.body}}
{{/list-accordion}}
{{/freestyle-usage}}

View File

@ -25,13 +25,13 @@
{{#each network.reservedPorts as |port|}}
<li data-test-port>
<strong>{{port.Label}}:</strong>
<a href="http://{{network.ip}}:{{port.Value}}" target="_blank">{{network.ip}}:{{port.Value}}</a>
<a href="http://{{network.ip}}:{{port.Value}}" target="_blank" rel="noopener noreferrer">{{network.ip}}:{{port.Value}}</a>
</li>
{{/each}}
{{#each network.dynamicPorts as |port|}}
<li>
<strong>{{port.Label}}:</strong>
<a href="http://{{network.ip}}:{{port.Value}}" target="_blank">{{network.ip}}:{{port.Value}}</a>
<a href="http://{{network.ip}}:{{port.Value}}" target="_blank" rel="noopener noreferrer">{{network.ip}}:{{port.Value}}</a>
</li>
{{/each}}
{{/with}}

View File

@ -16,7 +16,7 @@ export default Mixin.create({
endOffset: null,
offsetParams: computed('endOffset', function() {
const endOffset = this.get('endOffset');
const endOffset = this.endOffset;
return endOffset
? { origin: 'start', offset: endOffset }
: { origin: 'end', offset: MAX_OUTPUT_LENGTH };
@ -26,8 +26,8 @@ export default Mixin.create({
fullUrl: computed('url', 'params', 'offsetParams', 'additionalParams', function() {
const queryParams = queryString.stringify(
assign({}, this.get('params'), this.get('offsetParams'), this.get('additionalParams'))
assign({}, this.params, this.offsetParams, this.additionalParams)
);
return `${this.get('url')}?${queryParams}`;
return `${this.url}?${queryParams}`;
}),
});

Some files were not shown because too many files have changed in this diff Show More