Add manually-converted classes

I don’t know why the codemod ignored these files 🧐
This commit is contained in:
Buck Doyle 2020-06-11 16:23:00 -05:00
parent 577e85b007
commit cd11cd290c
44 changed files with 740 additions and 642 deletions

View File

@ -1,6 +1,6 @@
import ApplicationAdapter from './application';
export default class Agent extends ApplicationAdapter {
export default class AgentAdapter extends ApplicationAdapter {
pathForType = () => 'agent/members';
urlForFindRecord() {

View File

@ -1,8 +1,8 @@
import Watchable from './watchable';
import addToPath from 'nomad-ui/utils/add-to-path';
export default Watchable.extend({
stop: adapterAction('/stop'),
export default class AllocationAdapter extends Watchable {
stop = adapterAction('/stop');
restart(allocation, taskName) {
const prefix = `${this.host || '/'}${this.urlPrefix()}`;
@ -10,22 +10,20 @@ export default Watchable.extend({
return this.ajax(url, 'PUT', {
data: taskName && { TaskName: taskName },
});
},
}
ls(model, path) {
return this.token
.authorizedRequest(`/v1/client/fs/ls/${model.id}?path=${encodeURIComponent(path)}`)
.then(handleFSResponse);
},
}
stat(model, path) {
return this.token
.authorizedRequest(
`/v1/client/fs/stat/${model.id}?path=${encodeURIComponent(path)}`
)
.authorizedRequest(`/v1/client/fs/stat/${model.id}?path=${encodeURIComponent(path)}`)
.then(handleFSResponse);
},
});
}
}
async function handleFSResponse(response) {
if (response.ok) {

View File

@ -9,7 +9,7 @@ import classic from 'ember-classic-decorator';
export const namespace = 'v1';
@classic
export default class Application extends RESTAdapter {
export default class ApplicationAdapter extends RESTAdapter {
namespace = namespace;
@service system;

View File

@ -1,6 +1,6 @@
import Watchable from './watchable';
export default class Deployment extends Watchable {
export default class DeploymentAdapter extends Watchable {
promote(deployment) {
const id = deployment.get('id');
const url = urlForAction(this.urlForFindRecord(id, 'deployment'), '/promote');

View File

@ -1,6 +1,6 @@
import Watchable from './watchable';
export default class JobSummary extends Watchable {
export default class JobSummaryAdapter extends Watchable {
urlForFindRecord(id, type, hash) {
const [name, namespace] = JSON.parse(id);
let url = super.urlForFindRecord(name, 'job', hash) + '/summary';

View File

@ -1,27 +1,27 @@
import WatchableNamespaceIDs from './watchable-namespace-ids';
import addToPath from 'nomad-ui/utils/add-to-path';
export default WatchableNamespaceIDs.extend({
relationshipFallbackLinks: Object.freeze({
export default class JobAdapter extends WatchableNamespaceIDs {
relationshipFallbackLinks = Object.freeze({
summary: '/summary',
}),
});
fetchRawDefinition(job) {
const url = this.urlForFindRecord(job.get('id'), 'job');
return this.ajax(url, 'GET');
},
}
forcePeriodic(job) {
if (job.get('periodic')) {
const url = addToPath(this.urlForFindRecord(job.get('id'), 'job'), '/periodic/force');
return this.ajax(url, 'POST');
}
},
}
stop(job) {
const url = this.urlForFindRecord(job.get('id'), 'job');
return this.ajax(url, 'DELETE');
},
}
parse(spec) {
const url = addToPath(this.urlForFindAll('job'), '/parse');
@ -31,7 +31,7 @@ export default WatchableNamespaceIDs.extend({
Canonicalize: true,
},
});
},
}
plan(job) {
const jobId = job.get('id') || job.get('_idBeforeSaving');
@ -48,7 +48,7 @@ export default WatchableNamespaceIDs.extend({
store.pushPayload('job-plan', { jobPlans: [json] });
return store.peekRecord('job-plan', jobId);
});
},
}
// Running a job doesn't follow REST create semantics so it's easier to
// treat it as an action.
@ -58,7 +58,7 @@ export default WatchableNamespaceIDs.extend({
Job: job.get('_newDefinitionJSON'),
},
});
},
}
update(job) {
const jobId = job.get('id') || job.get('_idBeforeSaving');
@ -68,5 +68,5 @@ export default WatchableNamespaceIDs.extend({
Job: job.get('_newDefinitionJSON'),
},
});
},
});
}
}

View File

@ -1,7 +1,7 @@
import ApplicationAdapter from './application';
import codesForError from '../utils/codes-for-error';
export default class Namespace extends ApplicationAdapter {
export default class NamespaceAdapter extends ApplicationAdapter {
findRecord(store, modelClass, id) {
return super.findRecord(...arguments).catch(error => {
const errorCodes = codesForError(error);

View File

@ -1,7 +1,7 @@
import Watchable from './watchable';
import addToPath from 'nomad-ui/utils/add-to-path';
export default class Node extends Watchable {
export default class NodeAdapter extends Watchable {
setEligible(node) {
return this.setEligibility(node, true);
}

View File

@ -1,7 +1,7 @@
import Watchable from './watchable';
export default Watchable.extend({
queryParamsToAttrs: Object.freeze({
export default class PluginAdapter extends Watchable {
queryParamsToAttrs = Object.freeze({
type: 'type',
}),
});
});
}

View File

@ -1,5 +1,5 @@
import { default as ApplicationAdapter, namespace } from './application';
export default class Policy extends ApplicationAdapter {
export default class PolicyAdapter extends ApplicationAdapter {
namespace = namespace + '/acl';
}

View File

@ -1,7 +1,7 @@
import { inject as service } from '@ember/service';
import { default as ApplicationAdapter, namespace } from './application';
export default class Token extends ApplicationAdapter {
export default class TokenAdapter extends ApplicationAdapter {
@service store;
namespace = namespace + '/acl';

View File

@ -1,8 +1,8 @@
import WatchableNamespaceIDs from './watchable-namespace-ids';
export default WatchableNamespaceIDs.extend({
queryParamsToAttrs: Object.freeze({
export default class VolumeAdapter extends WatchableNamespaceIDs {
queryParamsToAttrs = Object.freeze({
type: 'type',
plugin_id: 'plugin.id',
}),
});
});
}

View File

@ -1,45 +1,45 @@
import { computed } from '@ember/object';
import DistributionBar from './distribution-bar';
export default DistributionBar.extend({
layoutName: 'components/distribution-bar',
export default class AllocationStatusBar extends DistributionBar {
layoutName = 'components/distribution-bar';
allocationContainer: null,
allocationContainer = null;
'data-test-allocation-status-bar': true,
'data-test-allocation-status-bar' = true;
data: computed(
'allocationContainer.{queuedAllocs,completeAllocs,failedAllocs,runningAllocs,startingAllocs}',
function() {
if (!this.allocationContainer) {
return [];
}
const allocs = this.allocationContainer.getProperties(
'queuedAllocs',
'completeAllocs',
'failedAllocs',
'runningAllocs',
'startingAllocs',
'lostAllocs'
);
return [
{ label: 'Queued', value: allocs.queuedAllocs, className: 'queued' },
{
label: 'Starting',
value: allocs.startingAllocs,
className: 'starting',
layers: 2,
},
{ label: 'Running', value: allocs.runningAllocs, className: 'running' },
{
label: 'Complete',
value: allocs.completeAllocs,
className: 'complete',
},
{ label: 'Failed', value: allocs.failedAllocs, className: 'failed' },
{ label: 'Lost', value: allocs.lostAllocs, className: 'lost' },
];
@computed(
'allocationContainer.{queuedAllocs,completeAllocs,failedAllocs,runningAllocs,startingAllocs}'
)
get data() {
if (!this.allocationContainer) {
return [];
}
),
});
const allocs = this.allocationContainer.getProperties(
'queuedAllocs',
'completeAllocs',
'failedAllocs',
'runningAllocs',
'startingAllocs',
'lostAllocs'
);
return [
{ label: 'Queued', value: allocs.queuedAllocs, className: 'queued' },
{
label: 'Starting',
value: allocs.startingAllocs,
className: 'starting',
layers: 2,
},
{ label: 'Running', value: allocs.runningAllocs, className: 'running' },
{
label: 'Complete',
value: allocs.completeAllocs,
className: 'complete',
},
{ label: 'Failed', value: allocs.failedAllocs, className: 'failed' },
{ label: 'Lost', value: allocs.lostAllocs, className: 'lost' },
];
}
}

View File

@ -1,6 +1,7 @@
/* eslint-disable ember/no-observers */
import Component from '@ember/component';
import { computed, observer, set } from '@ember/object';
import { computed, set } from '@ember/object';
import { observes } from '@ember-decorators/object';
import { run } from '@ember/runloop';
import { assign } from '@ember/polyfills';
import { guidFor } from '@ember/object/internals';
@ -9,22 +10,25 @@ import d3 from 'd3-selection';
import 'd3-transition';
import WindowResizable from '../mixins/window-resizable';
import styleStringProperty from '../utils/properties/style-string';
import { classNames, classNameBindings } from '@ember-decorators/component';
import classic from 'ember-classic-decorator';
const sumAggregate = (total, val) => total + val;
export default Component.extend(WindowResizable, {
classNames: ['chart', 'distribution-bar'],
classNameBindings: ['isNarrow:is-narrow'],
@classic
@classNames('chart', 'distribution-bar')
@classNameBindings('isNarrow:is-narrow')
export default class DistributionBar extends Component.extend(WindowResizable) {
chart = null;
data = null;
activeDatum = null;
isNarrow = false;
chart: null,
data: null,
activeDatum: null,
isNarrow: false,
@styleStringProperty('tooltipPosition') tooltipStyle;
maskId = null;
tooltipStyle: styleStringProperty('tooltipPosition'),
maskId: null,
_data: computed('data', function() {
@computed('data')
get _data() {
const data = copy(this.data, true);
const sum = data.mapBy('value').reduce(sumAggregate, 0);
@ -41,7 +45,7 @@ export default Component.extend(WindowResizable, {
.mapBy('value')
.reduce(sumAggregate, 0) / sum,
}));
}),
}
didInsertElement() {
const svg = this.element.querySelector('svg');
@ -63,15 +67,16 @@ export default Component.extend(WindowResizable, {
});
this.renderChart();
},
}
didUpdateAttrs() {
this.renderChart();
},
}
updateChart: observer('_data.@each.{value,label,className}', function() {
@observes('_data.@each.{value,label,className}')
updateChart() {
this.renderChart();
}),
}
// prettier-ignore
/* eslint-disable */
@ -166,10 +171,10 @@ export default Component.extend(WindowResizable, {
.attr('height', '6px')
.attr('y', '50%');
}
},
}
/* eslint-enable */
windowResizeHandler() {
run.once(this, this.renderChart);
},
});
}
}

View File

@ -1,16 +1,19 @@
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import { action, computed } from '@ember/object';
import { filterBy, mapBy, or, sort } from '@ember/object/computed';
import generateExecUrl from 'nomad-ui/utils/generate-exec-url';
import openExecUrl from 'nomad-ui/utils/open-exec-url';
import classic from 'ember-classic-decorator';
export default Component.extend({
router: service(),
@classic
export default class TaskGroupParent extends Component {
@service router;
isOpen: or('clickedOpen', 'currentRouteIsThisTaskGroup'),
@or('clickedOpen', 'currentRouteIsThisTaskGroup') isOpen;
currentRouteIsThisTaskGroup: computed('router.currentRoute', function() {
@computed('router.currentRoute')
get currentRouteIsThisTaskGroup() {
const route = this.router.currentRoute;
if (route.name.includes('task-group')) {
@ -24,58 +27,60 @@ export default Component.extend({
} else {
return false;
}
}),
}
hasPendingAllocations: computed('taskGroup.allocations.@each.clientStatus', function() {
@computed('taskGroup.allocations.@each.clientStatus')
get hasPendingAllocations() {
return this.taskGroup.allocations.any(allocation => allocation.clientStatus === 'pending');
}),
}
allocationTaskStatesRecordArrays: mapBy('taskGroup.allocations', 'states'),
allocationTaskStates: computed('allocationTaskStatesRecordArrays.[]', function() {
@mapBy('taskGroup.allocations', 'states') allocationTaskStatesRecordArrays;
@computed('allocationTaskStatesRecordArrays.[]')
get allocationTaskStates() {
const flattenRecordArrays = (accumulator, recordArray) =>
accumulator.concat(recordArray.toArray());
return this.allocationTaskStatesRecordArrays.reduce(flattenRecordArrays, []);
}),
}
activeTaskStates: filterBy('allocationTaskStates', 'isActive'),
@filterBy('allocationTaskStates', 'isActive') activeTaskStates;
activeTasks: mapBy('activeTaskStates', 'task'),
activeTaskGroups: mapBy('activeTasks', 'taskGroup'),
@mapBy('activeTaskStates', 'task') activeTasks;
@mapBy('activeTasks', 'taskGroup') activeTaskGroups;
tasksWithRunningStates: computed(
@computed(
'taskGroup.name',
'activeTaskStates.@each.name',
'activeTasks.@each.name',
'activeTaskGroups.@each.name',
function() {
const activeTaskStateNames = this.activeTaskStates
.filter(taskState => {
return taskState.task && taskState.task.taskGroup.name === this.taskGroup.name;
})
.mapBy('name');
'activeTaskGroups.@each.name'
)
get tasksWithRunningStates() {
const activeTaskStateNames = this.activeTaskStates
.filter(taskState => {
return taskState.task && taskState.task.taskGroup.name === this.taskGroup.name;
})
.mapBy('name');
return this.taskGroup.tasks.filter(task => activeTaskStateNames.includes(task.name));
}
),
return this.taskGroup.tasks.filter(task => activeTaskStateNames.includes(task.name));
}
taskSorting: Object.freeze(['name']),
sortedTasks: sort('tasksWithRunningStates', 'taskSorting'),
taskSorting = Object.freeze(['name']);
@sort('tasksWithRunningStates', 'taskSorting') sortedTasks;
clickedOpen: false,
clickedOpen = false;
actions: {
toggleOpen() {
this.toggleProperty('clickedOpen');
},
@action
toggleOpen() {
this.toggleProperty('clickedOpen');
}
openInNewWindow(job, taskGroup, task) {
let url = generateExecUrl(this.router, {
job,
taskGroup,
task,
});
@action
openInNewWindow(job, taskGroup, task) {
let url = generateExecUrl(this.router, {
job,
taskGroup,
task,
});
openExecUrl(url);
},
},
});
openExecUrl(url);
}
}

View File

@ -1,6 +1,7 @@
/* eslint-disable ember/no-observers */
import Component from '@ember/component';
import { computed, observer } from '@ember/object';
import { computed } from '@ember/object';
import { observes } from '@ember-decorators/object';
import { computed as overridable } from 'ember-overridable-computed';
import { guidFor } from '@ember/object/internals';
import { run } from '@ember/runloop';
@ -13,6 +14,8 @@ import d3Format from 'd3-format';
import d3TimeFormat from 'd3-time-format';
import WindowResizable from 'nomad-ui/mixins/window-resizable';
import styleStringProperty from 'nomad-ui/utils/properties/style-string';
import { classNames } from '@ember-decorators/component';
import classic from 'ember-classic-decorator';
// Returns a new array with the specified number of points linearly
// distributed across the bounds
@ -28,68 +31,73 @@ const lerp = ([low, high], numPoints) => {
// Round a number or an array of numbers
const nice = val => (val instanceof Array ? val.map(nice) : Math.round(val));
export default Component.extend(WindowResizable, {
classNames: ['chart', 'line-chart'],
@classic
@classNames('chart', 'line-chart')
export default class LineChart extends Component.extend(WindowResizable) {
// Public API
data: null,
xProp: null,
yProp: null,
timeseries: false,
chartClass: 'is-primary',
data = null;
xProp = null;
yProp = null;
timeseries = false;
chartClass = 'is-primary';
title: 'Line Chart',
description: null,
title = 'Line Chart';
description = null;
// Private Properties
width: 0,
height: 0,
width = 0;
height = 0;
isActive: false,
isActive = false;
fillId: computed(function() {
@computed()
get fillId() {
return `line-chart-fill-${guidFor(this)}`;
}),
}
maskId: computed(function() {
@computed()
get maskId() {
return `line-chart-mask-${guidFor(this)}`;
}),
}
activeDatum: null,
activeDatum = null;
activeDatumLabel: computed('activeDatum', function() {
@computed('activeDatum')
get activeDatumLabel() {
const datum = this.activeDatum;
if (!datum) return;
if (!datum) return undefined;
const x = datum[this.xProp];
return this.xFormat(this.timeseries)(x);
}),
}
activeDatumValue: computed('activeDatum', function() {
@computed('activeDatum')
get activeDatumValue() {
const datum = this.activeDatum;
if (!datum) return;
if (!datum) return undefined;
const y = datum[this.yProp];
return this.yFormat()(y);
}),
}
// Overridable functions that retrurn formatter functions
xFormat(timeseries) {
return timeseries ? d3TimeFormat.timeFormat('%b') : d3Format.format(',');
},
}
yFormat() {
return d3Format.format(',.2~r');
},
}
tooltipPosition: null,
tooltipStyle: styleStringProperty('tooltipPosition'),
tooltipPosition = null;
@styleStringProperty('tooltipPosition') tooltipStyle;
xScale: computed('data.[]', 'xProp', 'timeseries', 'yAxisOffset', function() {
@computed('data.[]', 'xProp', 'timeseries', 'yAxisOffset')
get xScale() {
const xProp = this.xProp;
const scale = this.timeseries ? d3Scale.scaleTime() : d3Scale.scaleLinear();
const data = this.data;
@ -99,25 +107,28 @@ export default Component.extend(WindowResizable, {
scale.rangeRound([10, this.yAxisOffset]).domain(domain);
return scale;
}),
}
xRange: computed('data.[]', 'xFormat', 'xProp', 'timeseries', function() {
@computed('data.[]', 'xFormat', 'xProp', 'timeseries')
get xRange() {
const { xProp, timeseries, data } = this;
const range = d3Array.extent(data, d => d[xProp]);
const formatter = this.xFormat(timeseries);
return range.map(formatter);
}),
}
yRange: computed('data.[]', 'yFormat', 'yProp', function() {
@computed('data.[]', 'yFormat', 'yProp')
get yRange() {
const yProp = this.yProp;
const range = d3Array.extent(this.data, d => d[yProp]);
const formatter = this.yFormat();
return range.map(formatter);
}),
}
yScale: computed('data.[]', 'yProp', 'xAxisOffset', function() {
@computed('data.[]', 'yProp', 'xAxisOffset')
get yScale() {
const yProp = this.yProp;
let max = d3Array.max(this.data, d => d[yProp]) || 1;
if (max > 1) {
@ -128,9 +139,10 @@ export default Component.extend(WindowResizable, {
.scaleLinear()
.rangeRound([this.xAxisOffset, 10])
.domain([0, max]);
}),
}
xAxis: computed('xScale', function() {
@computed('xScale')
get xAxis() {
const formatter = this.xFormat(this.timeseries);
return d3Axis
@ -138,17 +150,19 @@ export default Component.extend(WindowResizable, {
.scale(this.xScale)
.ticks(5)
.tickFormat(formatter);
}),
}
yTicks: computed('xAxisOffset', function() {
@computed('xAxisOffset')
get yTicks() {
const height = this.xAxisOffset;
const tickCount = Math.ceil(height / 120) * 2 + 1;
const domain = this.yScale.domain();
const ticks = lerp(domain, tickCount);
return domain[1] - domain[0] > 1 ? nice(ticks) : ticks;
}),
}
yAxis: computed('yScale', function() {
@computed('yScale')
get yAxis() {
const formatter = this.yFormat();
return d3Axis
@ -156,9 +170,10 @@ export default Component.extend(WindowResizable, {
.scale(this.yScale)
.tickValues(this.yTicks)
.tickFormat(formatter);
}),
}
yGridlines: computed('yScale', function() {
@computed('yScale')
get yGridlines() {
// The first gridline overlaps the x-axis, so remove it
const [, ...ticks] = this.yTicks;
@ -168,33 +183,38 @@ export default Component.extend(WindowResizable, {
.tickValues(ticks)
.tickSize(-this.yAxisOffset)
.tickFormat('');
}),
}
xAxisHeight: computed(function() {
@computed()
get xAxisHeight() {
// Avoid divide by zero errors by always having a height
if (!this.element) return 1;
const axis = this.element.querySelector('.x-axis');
return axis && axis.getBBox().height;
}),
}
yAxisWidth: computed(function() {
@computed()
get yAxisWidth() {
// Avoid divide by zero errors by always having a width
if (!this.element) return 1;
const axis = this.element.querySelector('.y-axis');
return axis && axis.getBBox().width;
}),
}
xAxisOffset: overridable('height', 'xAxisHeight', function() {
@overridable('height', 'xAxisHeight', function() {
return this.height - this.xAxisHeight;
}),
})
xAxisOffset;
yAxisOffset: computed('width', 'yAxisWidth', function() {
@computed('width', 'yAxisWidth')
get yAxisOffset() {
return this.width - this.yAxisWidth;
}),
}
line: computed('data.[]', 'xScale', 'yScale', function() {
@computed('data.[]', 'xScale', 'yScale')
get line() {
const { xScale, yScale, xProp, yProp } = this;
const line = d3Shape
@ -204,9 +224,10 @@ export default Component.extend(WindowResizable, {
.y(d => yScale(d[yProp]));
return line(this.data);
}),
}
area: computed('data.[]', 'xScale', 'yScale', function() {
@computed('data.[]', 'xScale', 'yScale')
get area() {
const { xScale, yScale, xProp, yProp } = this;
const area = d3Shape
@ -217,7 +238,7 @@ export default Component.extend(WindowResizable, {
.y1(d => yScale(d[yProp]));
return area(this.data);
}),
}
didInsertElement() {
this.updateDimensions();
@ -243,11 +264,11 @@ export default Component.extend(WindowResizable, {
run.schedule('afterRender', this, () => this.set('isActive', false));
this.set('activeDatum', null);
});
},
}
didUpdateAttrs() {
this.renderChart();
},
}
updateActiveDatum(mouseX) {
const { xScale, xProp, yScale, yProp, data } = this;
@ -278,11 +299,12 @@ export default Component.extend(WindowResizable, {
left: xScale(datum[xProp]),
top: yScale(datum[yProp]) - 10,
});
},
}
updateChart: observer('data.[]', function() {
@observes('data.[]')
updateChart() {
this.renderChart();
}),
}
// The renderChart method should only ever be responsible for runtime calculations
// and appending d3 created elements to the DOM (such as axes).
@ -308,7 +330,7 @@ export default Component.extend(WindowResizable, {
this.updateActiveDatum(this.latestMouseX);
}
});
},
}
mountD3Elements() {
if (!this.isDestroyed && !this.isDestroying) {
@ -316,11 +338,11 @@ export default Component.extend(WindowResizable, {
d3.select(this.element.querySelector('.y-axis')).call(this.yAxis);
d3.select(this.element.querySelector('.y-gridlines')).call(this.yGridlines);
}
},
}
windowResizeHandler() {
run.once(this, this.updateDimensions);
},
}
updateDimensions() {
const $svg = this.element.querySelector('svg');
@ -329,5 +351,5 @@ export default Component.extend(WindowResizable, {
this.setProperties({ width, height });
this.renderChart();
},
});
}
}

View File

@ -1,11 +1,11 @@
import Component from '@ember/component';
import { inject as service } from '@ember/service';
export default Component.extend({
userSettings: service(),
export default class PageSizeSelect extends Component {
@service userSettings;
tagName: '',
pageSizeOptions: Object.freeze([10, 25, 50]),
tagName = '';
pageSizeOptions = Object.freeze([10, 25, 50]);
onChange() {},
});
onChange() {}
}

View File

@ -1,58 +1,68 @@
/* eslint-disable ember/no-observers */
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { computed, observer } from '@ember/object';
import { action, computed } from '@ember/object';
import { observes } from '@ember-decorators/object';
import { computed as overridable } from 'ember-overridable-computed';
import { alias } from '@ember/object/computed';
import { task } from 'ember-concurrency';
import Sortable from 'nomad-ui/mixins/sortable';
import { lazyClick } from 'nomad-ui/helpers/lazy-click';
import { watchRecord } from 'nomad-ui/utils/properties/watch';
import classic from 'ember-classic-decorator';
export default Controller.extend(Sortable, {
token: service(),
@classic
export default class IndexController extends Controller.extend(Sortable) {
@service token;
queryParams: {
sortProperty: 'sort',
sortDescending: 'desc',
},
queryParams = [
{
sortProperty: 'sort',
},
{
sortDescending: 'desc',
},
];
sortProperty: 'name',
sortDescending: false,
sortProperty = 'name';
sortDescending = false;
listToSort: alias('model.states'),
sortedStates: alias('listSorted'),
@alias('model.states') listToSort;
@alias('listSorted') sortedStates;
// Set in the route
preempter: null,
preempter = null;
error: overridable(() => {
@overridable(function() {
// { title, description }
return null;
}),
})
error;
network: alias('model.allocatedResources.networks.firstObject'),
@alias('model.allocatedResources.networks.firstObject') network;
services: computed('model.taskGroup.services.@each.name', function() {
@computed('model.taskGroup.services.@each.name')
get services() {
return this.get('model.taskGroup.services').sortBy('name');
}),
}
onDismiss() {
this.set('error', null);
},
}
watchNext: watchRecord('allocation'),
@watchRecord('allocation') watchNext;
observeWatchNext: observer('model.nextAllocation.clientStatus', function() {
@observes('model.nextAllocation.clientStatus')
observeWatchNext() {
const nextAllocation = this.model.nextAllocation;
if (nextAllocation && nextAllocation.content) {
this.watchNext.perform(nextAllocation);
} else {
this.watchNext.cancelAll();
}
}),
}
stopAllocation: task(function*() {
@task(function*() {
try {
yield this.model.stop();
// Eagerly update the allocation clientStatus to avoid flickering
@ -63,9 +73,10 @@ export default Controller.extend(Sortable, {
description: 'Your ACL token does not grant allocation lifecycle permissions.',
});
}
}),
})
stopAllocation;
restartAllocation: task(function*() {
@task(function*() {
try {
yield this.model.restart();
} catch (err) {
@ -74,15 +85,16 @@ export default Controller.extend(Sortable, {
description: 'Your ACL token does not grant allocation lifecycle permissions.',
});
}
}),
})
restartAllocation;
actions: {
gotoTask(allocation, task) {
this.transitionToRoute('allocations.allocation.task', task);
},
@action
gotoTask(allocation, task) {
this.transitionToRoute('allocations.allocation.task', task);
}
taskClick(allocation, task, event) {
lazyClick([() => this.send('gotoTask', allocation, task), event]);
},
},
});
@action
taskClick(allocation, task, event) {
lazyClick([() => this.send('gotoTask', allocation, task), event]);
}
}

View File

@ -1,84 +1,99 @@
/* eslint-disable ember/no-observers */
import { alias } from '@ember/object/computed';
import Controller from '@ember/controller';
import { computed, observer } from '@ember/object';
import { action, computed } from '@ember/object';
import { observes } from '@ember-decorators/object';
import { task } from 'ember-concurrency';
import Sortable from 'nomad-ui/mixins/sortable';
import Searchable from 'nomad-ui/mixins/searchable';
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
import classic from 'ember-classic-decorator';
export default Controller.extend(Sortable, Searchable, {
queryParams: {
currentPage: 'page',
searchTerm: 'search',
sortProperty: 'sort',
sortDescending: 'desc',
onlyPreemptions: 'preemptions',
},
@classic
export default class ClientController extends Controller.extend(Sortable, Searchable) {
queryParams = [
{
currentPage: 'page',
},
{
searchTerm: 'search',
},
{
sortProperty: 'sort',
},
{
sortDescending: 'desc',
},
{
onlyPreemptions: 'preemptions',
},
];
// Set in the route
flagAsDraining: false,
flagAsDraining = false;
currentPage: 1,
pageSize: 8,
currentPage = 1;
pageSize = 8;
sortProperty: 'modifyIndex',
sortDescending: true,
sortProperty = 'modifyIndex';
sortDescending = true;
searchProps: computed(function() {
@computed()
get searchProps() {
return ['shortId', 'name'];
}),
}
onlyPreemptions: false,
onlyPreemptions = false;
visibleAllocations: computed(
'model.allocations.[]',
'preemptions.[]',
'onlyPreemptions',
function() {
return this.onlyPreemptions ? this.preemptions : this.model.allocations;
}
),
@computed('model.allocations.[]', 'preemptions.[]', 'onlyPreemptions')
get visibleAllocations() {
return this.onlyPreemptions ? this.preemptions : this.model.allocations;
}
listToSort: alias('visibleAllocations'),
listToSearch: alias('listSorted'),
sortedAllocations: alias('listSearched'),
@alias('visibleAllocations') listToSort;
@alias('listSorted') listToSearch;
@alias('listSearched') sortedAllocations;
eligibilityError: null,
stopDrainError: null,
drainError: null,
showDrainNotification: false,
showDrainUpdateNotification: false,
showDrainStoppedNotification: false,
eligibilityError = null;
stopDrainError = null;
drainError = null;
showDrainNotification = false;
showDrainUpdateNotification = false;
showDrainStoppedNotification = false;
preemptions: computed('model.allocations.@each.wasPreempted', function() {
@computed('model.allocations.@each.wasPreempted')
get preemptions() {
return this.model.allocations.filterBy('wasPreempted');
}),
}
sortedEvents: computed('model.events.@each.time', function() {
@computed('model.events.@each.time')
get sortedEvents() {
return this.get('model.events')
.sortBy('time')
.reverse();
}),
}
sortedDrivers: computed('model.drivers.@each.name', function() {
@computed('model.drivers.@each.name')
get sortedDrivers() {
return this.get('model.drivers').sortBy('name');
}),
}
sortedHostVolumes: computed('model.hostVolumes.@each.name', function() {
@computed('model.hostVolumes.@each.name')
get sortedHostVolumes() {
return this.model.hostVolumes.sortBy('name');
}),
}
setEligibility: task(function*(value) {
@(task(function*(value) {
try {
yield value ? this.model.setEligible() : this.model.setIneligible();
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not set eligibility';
this.set('eligibilityError', error);
}
}).drop(),
}).drop())
setEligibility;
stopDrain: task(function*() {
@(task(function*() {
try {
this.set('flagAsDraining', false);
yield this.model.cancelDrain();
@ -88,9 +103,10 @@ export default Controller.extend(Sortable, Searchable, {
const error = messageFromAdapterError(err) || 'Could not stop drain';
this.set('stopDrainError', error);
}
}).drop(),
}).drop())
stopDrain;
forceDrain: task(function*() {
@(task(function*() {
try {
yield this.model.forceDrain({
IgnoreSystemJobs: this.model.drainStrategy.ignoreSystemJobs,
@ -99,32 +115,36 @@ export default Controller.extend(Sortable, Searchable, {
const error = messageFromAdapterError(err) || 'Could not force drain';
this.set('drainError', error);
}
}).drop(),
}).drop())
forceDrain;
triggerDrainNotification: observer('model.isDraining', function() {
@observes('model.isDraining')
triggerDrainNotification() {
if (!this.model.isDraining && this.flagAsDraining) {
this.set('showDrainNotification', true);
}
this.set('flagAsDraining', this.model.isDraining);
}),
}
actions: {
gotoAllocation(allocation) {
this.transitionToRoute('allocations.allocation', allocation);
},
@action
gotoAllocation(allocation) {
this.transitionToRoute('allocations.allocation', allocation);
}
setPreemptionFilter(value) {
this.set('onlyPreemptions', value);
},
@action
setPreemptionFilter(value) {
this.set('onlyPreemptions', value);
}
drainNotify(isUpdating) {
this.set('showDrainUpdateNotification', isUpdating);
},
@action
drainNotify(isUpdating) {
this.set('showDrainUpdateNotification', isUpdating);
}
drainError(err) {
const error = messageFromAdapterError(err) || 'Could not run drain';
this.set('drainError', error);
},
},
});
@action
drainError(err) {
const error = messageFromAdapterError(err) || 'Could not run drain';
this.set('drainError', error);
}
}

View File

@ -1,36 +1,39 @@
import { inject as service } from '@ember/service';
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import { action, computed } from '@ember/object';
import { alias, mapBy, sort, uniq } from '@ember/object/computed';
import escapeTaskName from 'nomad-ui/utils/escape-task-name';
import ExecCommandEditorXtermAdapter from 'nomad-ui/utils/classes/exec-command-editor-xterm-adapter';
import ExecSocketXtermAdapter from 'nomad-ui/utils/classes/exec-socket-xterm-adapter';
import localStorageProperty from 'nomad-ui/utils/properties/local-storage';
import classic from 'ember-classic-decorator';
const ANSI_UI_GRAY_400 = '\x1b[38;2;142;150;163m';
const ANSI_WHITE = '\x1b[0m';
export default Controller.extend({
sockets: service(),
system: service(),
token: service(),
@classic
export default class ExecController extends Controller {
@service sockets;
@service system;
@service token;
queryParams: ['allocation'],
queryParams = ['allocation'];
command: localStorageProperty('nomadExecCommand', '/bin/bash'),
socketOpen: false,
@localStorageProperty('nomadExecCommand', '/bin/bash') command;
socketOpen = false;
pendingAndRunningAllocations: computed('model.allocations.@each.clientStatus', function() {
@computed('model.allocations.@each.clientStatus')
get pendingAndRunningAllocations() {
return this.model.allocations.filter(
allocation => allocation.clientStatus === 'pending' || allocation.clientStatus === 'running'
);
}),
}
pendingAndRunningTaskGroups: mapBy('pendingAndRunningAllocations', 'taskGroup'),
uniquePendingAndRunningTaskGroups: uniq('pendingAndRunningTaskGroups'),
@mapBy('pendingAndRunningAllocations', 'taskGroup') pendingAndRunningTaskGroups;
@uniq('pendingAndRunningTaskGroups') uniquePendingAndRunningTaskGroups;
taskGroupSorting: Object.freeze(['name']),
sortedTaskGroups: sort('uniquePendingAndRunningTaskGroups', 'taskGroupSorting'),
taskGroupSorting = Object.freeze(['name']);
@sort('uniquePendingAndRunningTaskGroups', 'taskGroupSorting') sortedTaskGroups;
setUpTerminal(Terminal) {
this.terminal = new Terminal({ fontFamily: 'monospace', fontWeight: '400' });
@ -38,86 +41,85 @@ export default Controller.extend({
this.terminal.write(ANSI_UI_GRAY_400);
this.terminal.writeln('Select a task to start your session.');
},
}
allocations: alias('model.allocations'),
@alias('model.allocations') allocations;
taskState: computed(
@computed(
'allocations.{[],@each.isActive}',
'allocationShortId',
'taskName',
'taskGroupName',
'allocation',
'allocation.states.@each.{name,isRunning}',
function() {
if (!this.allocations) {
return false;
}
let allocation;
if (this.allocationShortId) {
allocation = this.allocations.findBy('shortId', this.allocationShortId);
} else {
allocation = this.allocations.find(allocation =>
allocation.states
.filterBy('isActive')
.mapBy('name')
.includes(this.taskName)
);
}
if (allocation) {
return allocation.states.find(state => state.name === this.taskName);
}
return;
'allocation.states.@each.{name,isRunning}'
)
get taskState() {
if (!this.allocations) {
return false;
}
),
actions: {
setTaskProperties({ allocationShortId, taskName, taskGroupName }) {
this.setProperties({
allocationShortId,
taskName,
taskGroupName,
});
let allocation;
if (this.taskState) {
this.terminal.write(ANSI_UI_GRAY_400);
this.terminal.writeln('');
if (this.allocationShortId) {
allocation = this.allocations.findBy('shortId', this.allocationShortId);
} else {
allocation = this.allocations.find(allocation =>
allocation.states
.filterBy('isActive')
.mapBy('name')
.includes(this.taskName)
);
}
if (!allocationShortId) {
this.terminal.writeln(
'Multiple instances of this task are running. The allocation below was selected by random draw.'
);
this.terminal.writeln('');
}
if (allocation) {
return allocation.states.find(state => state.name === this.taskName);
}
this.terminal.writeln('Customize your command, then hit return to run.');
this.terminal.writeln('');
this.terminal.write(
`$ nomad alloc exec -i -t -task ${escapeTaskName(taskName)} ${
this.taskState.allocation.shortId
} `
);
this.terminal.write(ANSI_WHITE);
this.terminal.write(this.command);
if (this.commandEditorAdapter) {
this.commandEditorAdapter.destroy();
}
this.commandEditorAdapter = new ExecCommandEditorXtermAdapter(
this.terminal,
this.openAndConnectSocket.bind(this),
this.command
return undefined;
}
@action
setTaskProperties({ allocationShortId, taskName, taskGroupName }) {
this.setProperties({
allocationShortId,
taskName,
taskGroupName,
});
if (this.taskState) {
this.terminal.write(ANSI_UI_GRAY_400);
this.terminal.writeln('');
if (!allocationShortId) {
this.terminal.writeln(
'Multiple instances of this task are running. The allocation below was selected by random draw.'
);
this.terminal.writeln('');
}
},
},
this.terminal.writeln('Customize your command, then hit return to run.');
this.terminal.writeln('');
this.terminal.write(
`$ nomad alloc exec -i -t -task ${escapeTaskName(taskName)} ${
this.taskState.allocation.shortId
} `
);
this.terminal.write(ANSI_WHITE);
this.terminal.write(this.command);
if (this.commandEditorAdapter) {
this.commandEditorAdapter.destroy();
}
this.commandEditorAdapter = new ExecCommandEditorXtermAdapter(
this.terminal,
this.openAndConnectSocket.bind(this),
this.command
);
}
}
openAndConnectSocket(command) {
if (this.taskState) {
@ -129,5 +131,5 @@ export default Controller.extend({
} else {
this.terminal.writeln(`Failed to open a socket because task ${this.taskName} is not active.`);
}
},
});
}
}

View File

@ -3,66 +3,69 @@ import { reads } from '@ember/object/computed';
import Controller from '@ember/controller';
import { getOwner } from '@ember/application';
import { alias } from '@ember/object/computed';
import { action } from '@ember/object';
import classic from 'ember-classic-decorator';
export default Controller.extend({
token: service(),
system: service(),
store: service(),
@classic
export default class Tokens extends Controller {
@service token;
@service system;
@service store;
secret: reads('token.secret'),
@reads('token.secret') secret;
tokenIsValid: false,
tokenIsInvalid: false,
tokenRecord: alias('token.selfToken'),
tokenIsValid = false;
tokenIsInvalid = false;
@alias('token.selfToken') tokenRecord;
resetStore() {
this.store.unloadAll();
},
}
actions: {
clearTokenProperties() {
this.token.setProperties({
secret: undefined,
});
this.setProperties({
tokenIsValid: false,
tokenIsInvalid: false,
});
this.resetStore();
this.token.reset();
},
@action
clearTokenProperties() {
this.token.setProperties({
secret: undefined,
});
this.setProperties({
tokenIsValid: false,
tokenIsInvalid: false,
});
this.resetStore();
this.token.reset();
}
verifyToken() {
const { secret } = this;
const TokenAdapter = getOwner(this).lookup('adapter:token');
@action
verifyToken() {
const { secret } = this;
const TokenAdapter = getOwner(this).lookup('adapter:token');
this.set('token.secret', secret);
this.set('token.secret', secret);
TokenAdapter.findSelf().then(
() => {
// Clear out all data to ensure only data the new token is privileged to
// see is shown
this.system.reset();
this.resetStore();
TokenAdapter.findSelf().then(
() => {
// Clear out all data to ensure only data the new token is privileged to
// see is shown
this.system.reset();
this.resetStore();
// Refetch the token and associated policies
this.get('token.fetchSelfTokenAndPolicies')
.perform()
.catch();
// Refetch the token and associated policies
this.get('token.fetchSelfTokenAndPolicies')
.perform()
.catch();
this.setProperties({
tokenIsValid: true,
tokenIsInvalid: false,
});
},
() => {
this.set('token.secret', undefined);
this.setProperties({
tokenIsValid: false,
tokenIsInvalid: true,
});
}
);
},
},
});
this.setProperties({
tokenIsValid: true,
tokenIsInvalid: false,
});
},
() => {
this.set('token.secret', undefined);
this.setProperties({
tokenIsValid: false,
tokenIsInvalid: true,
});
}
);
}
}

View File

@ -3,24 +3,27 @@ import { next } from '@ember/runloop';
import Route from '@ember/routing/route';
import { AbortError } from '@ember-data/adapter/error';
import RSVP from 'rsvp';
import { action } from '@ember/object';
import classic from 'ember-classic-decorator';
export default Route.extend({
config: service(),
system: service(),
store: service(),
token: service(),
@classic
export default class ApplicationRoute extends Route {
@service config;
@service system;
@service store;
@service token;
queryParams: {
queryParams = {
region: {
refreshModel: true,
},
},
};
resetController(controller, isExiting) {
if (isExiting) {
controller.set('error', null);
}
},
}
beforeModel(transition) {
const fetchSelfTokenAndPolicies = this.get('token.fetchSelfTokenAndPolicies')
@ -51,13 +54,13 @@ export default Route.extend({
return promises;
});
},
}
// Model is being used as a way to transfer the provided region
// query param to update the controller state.
model(params) {
return params.region;
},
}
setupController(controller, model) {
const queryParam = model;
@ -68,24 +71,25 @@ export default Route.extend({
});
}
return this._super(...arguments);
},
return super.setupController(...arguments);
}
actions: {
didTransition() {
if (!this.get('config.isTest')) {
window.scrollTo(0, 0);
}
},
@action
didTransition() {
if (!this.get('config.isTest')) {
window.scrollTo(0, 0);
}
}
willTransition() {
this.controllerFor('application').set('error', null);
},
@action
willTransition() {
this.controllerFor('application').set('error', null);
}
error(error) {
if (!(error instanceof AbortError)) {
this.controllerFor('application').set('error', error);
}
},
},
});
@action
error(error) {
if (!(error instanceof AbortError)) {
this.controllerFor('application').set('error', error);
}
}
}

View File

@ -3,26 +3,28 @@ import Route from '@ember/routing/route';
import RSVP from 'rsvp';
import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state';
import notifyForbidden from 'nomad-ui/utils/notify-forbidden';
import classic from 'ember-classic-decorator';
export default Route.extend(WithForbiddenState, {
store: service(),
system: service(),
@classic
export default class ClientsRoute extends Route.extend(WithForbiddenState) {
@service store;
@service system;
breadcrumbs: Object.freeze([
breadcrumbs = Object.freeze([
{
label: 'Clients',
args: ['clients.index'],
},
]),
]);
beforeModel() {
return this.get('system.leader');
},
}
model() {
return RSVP.hash({
nodes: this.store.findAll('node'),
agents: this.store.findAll('agent'),
}).catch(notifyForbidden(this));
},
});
}
}

View File

@ -3,17 +3,17 @@ import Route from '@ember/routing/route';
import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state';
import notifyForbidden from 'nomad-ui/utils/notify-forbidden';
export default Route.extend(WithForbiddenState, {
store: service(),
export default class PluginsRoute extends Route.extend(WithForbiddenState) {
@service store;
breadcrumbs: Object.freeze([
breadcrumbs = Object.freeze([
{
label: 'Storage',
args: ['csi.index'],
},
]),
]);
model() {
return this.store.query('plugin', { type: 'csi' }).catch(notifyForbidden(this));
},
});
}
}

View File

@ -2,23 +2,25 @@ import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state';
import notifyForbidden from 'nomad-ui/utils/notify-forbidden';
import classic from 'ember-classic-decorator';
export default Route.extend(WithForbiddenState, {
system: service(),
store: service(),
@classic
export default class VolumesRoute extends Route.extend(WithForbiddenState) {
@service system;
@service store;
breadcrumbs: Object.freeze([
breadcrumbs = Object.freeze([
{
label: 'Storage',
args: ['csi.index'],
},
]),
]);
queryParams: {
queryParams = {
volumeNamespace: {
refreshModel: true,
},
},
};
beforeModel(transition) {
return this.get('system.namespaces').then(namespaces => {
@ -27,7 +29,7 @@ export default Route.extend(WithForbiddenState, {
return namespaces;
});
},
}
model() {
return this.store
@ -37,5 +39,5 @@ export default Route.extend(WithForbiddenState, {
return volumes;
})
.catch(notifyForbidden(this));
},
});
}
}

View File

@ -4,14 +4,16 @@ import notifyError from 'nomad-ui/utils/notify-error';
import { collect } from '@ember/object/computed';
import WithWatchers from 'nomad-ui/mixins/with-watchers';
import { watchRecord, watchRelationship } from 'nomad-ui/utils/properties/watch';
import classic from 'ember-classic-decorator';
export default Route.extend(WithWatchers, {
store: service(),
token: service(),
@classic
export default class ExecRoute extends Route.extend(WithWatchers) {
@service store;
@service token;
serialize(model) {
return { job_name: model.get('plainId') };
},
}
model(params, transition) {
const namespace = transition.to.queryParams.namespace || this.get('system.activeNamespace.id');
@ -28,22 +30,22 @@ export default Route.extend(WithWatchers, {
const xtermImport = import('xterm').then(module => module.Terminal);
return Promise.all([jobPromise, xtermImport]);
},
}
setupController(controller, [job, Terminal]) {
this._super(controller, job);
super.setupController(controller, job);
controller.setUpTerminal(Terminal);
},
}
startWatchers(controller, model) {
if (model) {
controller.set('watcher', this.watch.perform(model));
controller.set('watchAllocations', this.watchAllocations.perform(model));
}
},
}
watch: watchRecord('job'),
watchAllocations: watchRelationship('allocations'),
@watchRecord('job') watch;
@watchRelationship('allocations') watchAllocations;
watchers: collect('watch', 'watchAllocations'),
});
@collect('watch', 'watchAllocations') watchers;
}

View File

@ -2,23 +2,26 @@ import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state';
import notifyForbidden from 'nomad-ui/utils/notify-forbidden';
import { action } from '@ember/object';
import classic from 'ember-classic-decorator';
export default Route.extend(WithForbiddenState, {
system: service(),
store: service(),
@classic
export default class JobsRoute extends Route.extend(WithForbiddenState) {
@service store;
@service system;
breadcrumbs: Object.freeze([
breadcrumbs = Object.freeze([
{
label: 'Jobs',
args: ['jobs.index'],
},
]),
]);
queryParams: {
queryParams = {
jobNamespace: {
refreshModel: true,
},
},
};
beforeModel(transition) {
return this.get('system.namespaces').then(namespaces => {
@ -27,15 +30,14 @@ export default Route.extend(WithForbiddenState, {
return namespaces;
});
},
}
model() {
return this.store.findAll('job', { reload: true }).catch(notifyForbidden(this));
},
}
actions: {
refreshRoute() {
this.refresh();
},
},
});
@action
refreshRoute() {
this.refresh();
}
}

View File

@ -1,33 +1,35 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import classic from 'ember-classic-decorator';
export default Route.extend({
can: service(),
store: service(),
system: service(),
@classic
export default class RunRoute extends Route {
@service can;
@service store;
@service system;
breadcrumbs: Object.freeze([
breadcrumbs = Object.freeze([
{
label: 'Run',
args: ['jobs.run'],
},
]),
]);
beforeModel() {
if (this.can.cannot('run job')) {
this.transitionTo('jobs');
}
},
}
model() {
return this.store.createRecord('job', {
namespace: this.get('system.activeNamespace'),
});
},
}
resetController(controller, isExiting) {
if (isExiting) {
controller.model.deleteRecord();
}
},
});
}
}

View File

@ -3,26 +3,28 @@ import Route from '@ember/routing/route';
import RSVP from 'rsvp';
import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state';
import notifyForbidden from 'nomad-ui/utils/notify-forbidden';
import classic from 'ember-classic-decorator';
export default Route.extend(WithForbiddenState, {
store: service(),
system: service(),
@classic
export default class ServersRoute extends Route.extend(WithForbiddenState) {
@service store;
@service system;
breadcrumbs: Object.freeze([
breadcrumbs = Object.freeze([
{
label: 'Servers',
args: ['servers.index'],
},
]),
]);
beforeModel() {
return this.get('system.leader');
},
}
model() {
return RSVP.hash({
nodes: this.store.findAll('node'),
agents: this.store.findAll('agent'),
}).catch(notifyForbidden(this));
},
});
}
}

View File

@ -1,12 +1,12 @@
import ApplicationSerializer from './application';
import AdapterError from '@ember-data/adapter/error';
export default ApplicationSerializer.extend({
attrs: {
export default class AgentSerializer extends ApplicationSerializer {
attrs = {
datacenter: 'dc',
address: 'Addr',
serfPort: 'Port',
},
};
normalize(typeHash, hash) {
if (!hash) {
@ -24,14 +24,14 @@ export default ApplicationSerializer.extend({
hash.Region = hash.Tags && hash.Tags.region;
hash.RpcPort = hash.Tags && hash.Tags.port;
return this._super(typeHash, hash);
},
return super.normalize(typeHash, hash);
}
normalizeResponse(store, typeClass, hash, ...args) {
return this._super(store, typeClass, hash.Members || [], ...args);
},
return super.normalizeResponse(store, typeClass, hash.Members || [], ...args);
}
normalizeSingleResponse(store, typeClass, hash, id, ...args) {
return this._super(store, typeClass, hash.findBy('Name', id), id, ...args);
},
});
return super.normalizeSingleResponse(store, typeClass, hash.findBy('Name', id), id, ...args);
}
}

View File

@ -8,13 +8,13 @@ const taskGroupFromJob = (job, taskGroupName) => {
return taskGroup ? taskGroup : null;
};
export default ApplicationSerializer.extend({
system: service(),
export default class AllocationSerializer extends ApplicationSerializer {
@service system;
attrs: {
attrs = {
taskGroupName: 'TaskGroup',
states: 'TaskStates',
},
};
normalize(typeHash, hash) {
// Transform the map-based TaskStates object into an array-based
@ -63,6 +63,6 @@ export default ApplicationSerializer.extend({
// The Job definition for an allocation is only included in findRecord responses.
hash.AllocationTaskGroup = !hash.Job ? null : taskGroupFromJob(hash.Job, hash.TaskGroup);
return this._super(typeHash, hash);
},
});
return super.normalize(typeHash, hash);
}
}

View File

@ -1,11 +1,13 @@
import { get } from '@ember/object';
import { assign } from '@ember/polyfills';
import ApplicationSerializer from './application';
import classic from 'ember-classic-decorator';
export default ApplicationSerializer.extend({
attrs: {
@classic
export default class DeploymentSerializer extends ApplicationSerializer {
attrs = {
versionNumber: 'JobVersion',
},
};
normalize(typeHash, hash) {
if (hash) {
@ -29,8 +31,8 @@ export default ApplicationSerializer.extend({
hash.JobID = hash.JobForLatestID = JSON.stringify([hash.JobID, hash.Namespace]);
}
return this._super(typeHash, hash);
},
return super.normalize(typeHash, hash);
}
extractRelationships(modelClass, hash) {
const namespace = this.store.adapterFor(modelClass.modelName).get('namespace');
@ -44,7 +46,7 @@ export default ApplicationSerializer.extend({
},
},
},
this._super(modelClass, hash)
super.extractRelationships(modelClass, hash)
);
},
});
}
}

View File

@ -1,10 +1,10 @@
import { assign } from '@ember/polyfills';
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
attrs: {
export default class JobVersionSerializer extends ApplicationSerializer {
attrs = {
number: 'Version',
},
};
normalizeFindHasManyResponse(store, modelClass, hash, id, requestType) {
const zippedVersions = hash.Versions.map((version, index) =>
@ -16,6 +16,13 @@ export default ApplicationSerializer.extend({
SubmitTimeNanos: version.SubmitTime % 1000000,
})
);
return this._super(store, modelClass, zippedVersions, hash, id, requestType);
},
});
return super.normalizeFindHasManyResponse(
store,
modelClass,
zippedVersions,
hash,
id,
requestType
);
}
}

View File

@ -2,10 +2,10 @@ import { assign } from '@ember/polyfills';
import ApplicationSerializer from './application';
import queryString from 'query-string';
export default ApplicationSerializer.extend({
attrs: {
export default class JobSerializer extends ApplicationSerializer {
attrs = {
parameterized: 'ParameterizedJob',
},
};
normalize(typeHash, hash) {
hash.NamespaceID = hash.Namespace;
@ -45,8 +45,8 @@ export default ApplicationSerializer.extend({
});
}
return this._super(typeHash, hash);
},
return super.normalize(typeHash, hash);
}
extractRelationships(modelClass, hash) {
const namespace =
@ -58,7 +58,7 @@ export default ApplicationSerializer.extend({
.buildURL(modelName, hash.ID, hash, 'findRecord')
.split('?');
return assign(this._super(...arguments), {
return assign(super.extractRelationships(...arguments), {
allocations: {
links: {
related: buildURL(`${jobURL}/allocations`, { namespace }),
@ -85,8 +85,8 @@ export default ApplicationSerializer.extend({
},
},
});
},
});
}
}
function buildURL(path, queryParams) {
const qpString = queryString.stringify(queryParams);

View File

@ -1,12 +1,12 @@
import ApplicationSerializer from './application';
import isIp from 'is-ip';
export default ApplicationSerializer.extend({
attrs: {
export default class NetworkSerializer extends ApplicationSerializer {
attrs = {
cidr: 'CIDR',
ip: 'IP',
mbits: 'MBits',
},
};
normalize(typeHash, hash) {
const ip = hash.IP;
@ -31,6 +31,6 @@ export default ApplicationSerializer.extend({
hash.Ports = reservedPorts.concat(dynamicPorts).sortBy('name');
return this._super(...arguments);
},
});
return super.normalize(...arguments);
}
}

View File

@ -1,7 +1,7 @@
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
attrs: {
export default class NodeEventSerializer extends ApplicationSerializer {
attrs = {
time: 'Timestamp',
},
});
};
}

View File

@ -2,13 +2,13 @@ import { assign } from '@ember/polyfills';
import { inject as service } from '@ember/service';
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
config: service(),
export default class NodeSerializer extends ApplicationSerializer {
@service config;
attrs: {
attrs = {
isDraining: 'Drain',
httpAddr: 'HTTPAddr',
},
};
normalize(modelClass, hash) {
// Transform map-based objects into array-based fragment lists
@ -20,8 +20,8 @@ export default ApplicationSerializer.extend({
const hostVolumes = hash.HostVolumes || {};
hash.HostVolumes = Object.keys(hostVolumes).map(key => hostVolumes[key]);
return this._super(modelClass, hash);
},
return super.normalize(modelClass, hash);
}
extractRelationships(modelClass, hash) {
const { modelName } = modelClass;
@ -36,5 +36,5 @@ export default ApplicationSerializer.extend({
},
},
};
},
});
}
}

View File

@ -1,10 +1,10 @@
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
attrs: {
export default class ResourcesSerializer extends ApplicationSerializer {
attrs = {
cpu: 'CPU',
memory: 'MemoryMB',
disk: 'DiskMB',
iops: 'IOPS',
},
});
};
}

View File

@ -1,15 +1,15 @@
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
attrs: {
export default class ServiceSerializer extends ApplicationSerializer {
attrs = {
connect: 'Connect',
},
};
normalize(typeHash, hash) {
if (!hash.Tags) {
hash.Tags = [];
}
return this._super(typeHash, hash);
},
});
return super.normalize(typeHash, hash);
}
}

View File

@ -1,9 +1,9 @@
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
attrs: {
export default class TaskEventSerializer extends ApplicationSerializer {
attrs = {
message: 'DisplayMessage',
},
};
normalize(typeHash, hash) {
// Time is in the form of nanoseconds since epoch, but JS dates
@ -13,6 +13,6 @@ export default ApplicationSerializer.extend({
hash.TimeNanos = hash.Time % 1000000;
hash.Time = Math.floor(hash.Time / 1000000);
return this._super(typeHash, hash);
},
});
return super.normalize(typeHash, hash);
}
}

View File

@ -1,16 +1,16 @@
import { copy } from 'ember-copy';
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
primaryKey: 'AccessorID',
export default class TokenSerializer extends ApplicationSerializer {
primaryKey = 'AccessorID';
attrs: {
attrs = {
secret: 'SecretID',
},
};
normalize(typeHash, hash) {
hash.PolicyIDs = hash.Policies;
hash.PolicyNames = copy(hash.Policies);
return this._super(typeHash, hash);
},
});
return super.normalize(typeHash, hash);
}
}

View File

@ -1,12 +1,12 @@
import { set, get } from '@ember/object';
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
attrs: {
export default class VolumeSerializer extends ApplicationSerializer {
attrs = {
externalId: 'ExternalID',
},
};
embeddedRelationships: Object.freeze(['writeAllocations', 'readAllocations']),
embeddedRelationships = Object.freeze(['writeAllocations', 'readAllocations']);
// Volumes treat Allocations as embedded records. Ember has an
// EmbeddedRecords mixin, but it assumes an application is using
@ -35,15 +35,15 @@ export default ApplicationSerializer.extend({
hash.ReadAllocations = Object.keys(readAllocs).map(bindIDToAlloc(readAllocs));
hash.WriteAllocations = Object.keys(writeAllocs).map(bindIDToAlloc(writeAllocs));
const normalizedHash = this._super(typeHash, hash);
const normalizedHash = super.normalize(typeHash, hash);
return this.extractEmbeddedRecords(this, this.store, typeHash, normalizedHash);
},
}
keyForRelationship(attr, relationshipType) {
//Embedded relationship attributes don't end in IDs
if (this.embeddedRelationships.includes(attr)) return attr.capitalize();
return this._super(attr, relationshipType);
},
return super.keyForRelationship(attr, relationshipType);
}
// Convert the embedded relationship arrays into JSONAPI included records
extractEmbeddedRecords(serializer, store, typeHash, partial) {
@ -84,7 +84,7 @@ export default ApplicationSerializer.extend({
});
return partial;
},
}
normalizeEmbeddedRelationship(store, relationshipMeta, relationshipHash) {
const modelName = relationshipMeta.type;
@ -92,5 +92,5 @@ export default ApplicationSerializer.extend({
const serializer = store.serializerFor(modelName);
return serializer.normalize(modelClass, relationshipHash, null);
},
});
}
}

View File

@ -4,19 +4,22 @@ import TextDecoder from 'nomad-ui/utils/classes/text-decoder';
import { decode } from 'nomad-ui/utils/stream-frames';
import AbstractLogger from './abstract-logger';
import { fetchFailure } from './log';
import classic from 'ember-classic-decorator';
export default EmberObject.extend(AbstractLogger, {
reader: null,
@classic
export default class StreamLogger extends EmberObject.extend(AbstractLogger) {
reader = null;
additionalParams: computed(function() {
@computed()
get additionalParams() {
return {
follow: true,
};
}),
}
start() {
return this.poll.perform();
},
}
stop() {
const reader = this.reader;
@ -24,9 +27,9 @@ export default EmberObject.extend(AbstractLogger, {
reader.cancel();
}
return this.poll.cancelAll();
},
}
poll: task(function*() {
@task(function*() {
const url = this.fullUrl;
const logFetch = this.logFetch;
@ -81,8 +84,11 @@ export default EmberObject.extend(AbstractLogger, {
}
});
}
}),
}).reopenClass({
})
poll;
}
StreamLogger.reopenClass({
isSupported: !!window.ReadableStream && !isSafari(),
});

View File

@ -2,6 +2,6 @@ import AdapterError from '@ember-data/adapter/error';
export const NO_LEADER = 'No cluster leader';
export default AdapterError.extend({
message: NO_LEADER,
});
export default class NoLeaderError extends AdapterError {
message = NO_LEADER;
}