ui: UI Release Merge (ui-staging merge) (#6527)

## HTTPAdapter (#5637)

## Ember upgrade 2.18 > 3.12 (#6448)

### Proxies can no longer get away with not calling _super

This means that we can't use create anymore to define dynamic methods.
Therefore we dynamically make 2 extended Proxies on demand, and then
create from those. Therefore we can call _super in the init method of
the extended Proxies.

### We aren't allowed to reset a service anymore

We never actually need to now anyway, this is a remnant of the refactor
from browser based confirmations. We fix it as simply as possible here
but will revisit and remove the old browser confirm functionality at a
later date

### Revert classes to use ES5 style to workaround babel transp. probs

Using a mixture of ES6 classes (and hence super) and arrow functions
means that when babel transpiles the arrow functions down to ES5, a
reference to this is moved before the call to super, hence causing a js
error.

Furthermore, we the testing environment no longer lets use use
apply/call on the constructor.

These errors only manifests during testing (only in the testing
environment), the application itself runs fine with no problems without
this change.

Using ES5 style class definitions give us freedom to do all of the above
without causing any errors, so we reverted these classes back to ES5
class definitions

### Skip test that seems to have changed due to a change in RSVP timing

This test tests a usecase/area of the API that will probably never ever
be used, it was more testing out the API. We've skipped the test for now
as this doesn't affect the application itself, but left a note to come
back here later to investigate further

### Remove enumerableContentDidChange

Initial testing looks like we don't need to call this function anymore,
the function no longer exists

### Rework Changeset.isSaving to take into account new ember APIs

Setting/hanging a computedProperty of an instantiated object no longer
works. Move to setting it on the prototype/class definition instead

### Change how we detect whether something requires listening

New ember API's have changed how you can detect whether something is a
computedProperty or not. It's not immediately clear if its even possible
now. Therefore we change how we detect whether something should be
listened to or not by just looking for presence of `addEventListener`

### Potentially temporary change of ci test scripts to ensure deps exist

All our tooling scripts run through a Makefile (for people familiar with
only using those), which then call yarn scripts which can be called
independently (for people familar with only using yarn).

The Makefile targets always check to make sure all the dependencies are
installed before running anything that requires them (building, testing
etc).

The CI scripts/targets didn't follow this same route and called the yarn
scripts directly (usually CI builds a cache of the dependencies first).

For some reason this cache isn't doing what it usually does, and it
looks as though, in CI, ember isn't installed.

This commit makes the CI scripts consistently use the same method as all
of the other tooling scripts (Makefile target > Install Deps if
required > call yarn script). This should install the dependencies if
for some reason the CI cache building doesn't complete/isn't successful.

Potentially this commit may be reverted if, the root of the problem is
elsewhere, although consistency is always good, so it might be a good
idea to leave this commit as is even if we need to debug and fix things
elsewhere.

### Make test-parallel consistent with the rest of the tooling scripts

As we are here making changes for CI purposes (making test-ci
consistent), we spotted that test-parallel is also inconsistent and also
the README manual instructions won't work without `ember` installed
globally.

This commit makes everything consistent and changes the manual
instructions to use the local ember instance that gets installed via
yarn

### Re-wrangle catchable to fit with new ember 3.12 APIs

In the upgrade from ember 3.8 > 3.12 the public interfaces for
ComputedProperties have changed slightly. `meta` is no longer a public
property of ComputedProperty but of a ComputedDecoratorImpl mixin
instead.

7e4ba1096e/packages/%40ember/-internals/metal/lib/computed.ts (L725)

There seems to be no way, by just using publically available
methods, to replicate this behaviour so that we can create our own
'ComputedProperty` factory via injecting the ComputedProperty class as
we did previously.

3f333bada1/ui-v2/app/utils/computed/factory.js (L1-L18)

Instead we dynamically hang our `Catchable` `catch` method off the
instantiated ComputedProperty. In doing it like this `ComputedProperty`
has already has its `meta` method mixed in so we don't have to manually
mix it in ourselves (which doesn't seem possible)

This functionality is only used during our work in trying to ensure
our EventSource/BlockingQuery work was as 'ember-like' as possible (i.e.
using the traditional Route.model hooks and ember-like Controller
properties). Our ongoing/upcoming work on a componentized approach to
data a.k.a `<DataSource />` means we will be able to remove the majority
of the code involved here now that it seems to be under an amount of
flux in ember.

### Build bindata_assetfs.go with new UI changes
This commit is contained in:
John Cowen 2019-09-30 14:47:49 +01:00 committed by GitHub
parent 2eaece10a7
commit b3b32dc0f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
495 changed files with 13411 additions and 13069 deletions

View File

@ -447,7 +447,7 @@ jobs:
- checkout
- restore_cache:
key: *YARN_CACHE_KEY
- run: cd ui-v2 && yarn build-ci --output-path=dist
- run: cd ui-v2 && make build-ci
# saves the build to a workspace to be passed to a downstream job
- persist_to_workspace:

File diff suppressed because one or more lines are too long

View File

@ -1 +1,21 @@
app/utils/dom/event-target/event-target-shim/event.js
# 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

@ -1,11 +1,8 @@
module.exports = {
root: true,
parserOptions: {
ecmaVersion: 2017,
sourceType: 'module',
ecmaFeatures: {
experimentalObjectRestSpread: true
}
ecmaVersion: 2018,
sourceType: 'module'
},
plugins: ['ember'],
extends: ['eslint:recommended', 'plugin:ember/recommended'],
@ -13,29 +10,38 @@ module.exports = {
browser: true,
},
rules: {
'no-unused-vars': ['error', { args: 'none' }]
'no-unused-vars': ['error', { args: 'none' }],
'ember/no-new-mixins': ['warn']
},
overrides: [
// node files
{
files: ['testem.js', 'ember-cli-build.js', 'config/**/*.js'],
files: [
'.eslintrc.js',
'.dev.eslintrc.js',
'.template-lintrc.js',
'ember-cli-build.js',
'testem.js',
'blueprints/*/index.js',
'config/**/*.js',
'lib/*/index.js',
'server/**/*.js'
],
parserOptions: {
sourceType: 'script',
ecmaVersion: 2015,
sourceType: 'script'
},
env: {
browser: false,
node: true,
node: true
},
},
plugins: ['node'],
rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
// add your custom rules and overrides for node files here
// test files
{
files: ['tests/**/*.js'],
excludedFiles: ['tests/dummy/**/*.js'],
env: {
embertest: true,
},
},
],
// this can be removed once the following is fixed
// https://github.com/mysticatea/eslint-plugin-node/issues/77
'node/no-unpublished-require': 'off'
})
}
]
};

34
ui-v2/.gitignore vendored
View File

@ -1,12 +1,26 @@
/dist
/tmp
/node_modules
/coverage/*
/npm-debug.log*
/yarn-error.log
/testem.log
/public/consul-api-double
# See https://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/.env*
/.pnp*
/.sass-cache
/connect.lock
/coverage/
/libpeerconnection.log
/npm-debug.log*
/testem.log
/yarn-error.log
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try

24
ui-v2/.template-lintrc.js Normal file
View File

@ -0,0 +1,24 @@
'use strict';
module.exports = {
extends: 'recommended',
rules: {
'no-partial': false,
'no-invalid-interactive': false,
'simple-unless': false,
'self-closing-void-elements': false,
'no-unnecessary-concat': false,
'no-nested-interactive': false,
'block-indentation': false,
'quotes': false,
'no-inline-styles': false,
'no-triple-curlies': false,
'no-unused-block-params': false,
'style-concatenation': false,
'link-rel-noopener': false
},
};

View File

@ -21,7 +21,7 @@ build-staging: deps
yarn run build:staging
build-ci: deps
yarn run build-ci --output-path=dist
yarn run build:ci --output-path=dist
build: deps
yarn run build
@ -39,10 +39,10 @@ test-view: deps
yarn run test:view
test-parallel: deps
yarn test-parallel
yarn run test:parallel
lint: deps
yarn run lint:js
yarn run lint:hbs && yarn run lint:js
format: deps
yarn run format:js

View File

@ -17,6 +17,8 @@ You will need the following things properly installed on your computer.
* `cd ui-v2`
* `yarn install`
All tooling scripts below primarily use `make` which in turn call node package scripts.
## Running / Development
The source code comes with a small server that runs enough of the consul API
@ -61,6 +63,19 @@ Please note: You do not need to run `make start-api`/`yarn run start:api` to run
* `make test` or `yarn run test`
* `make test-view` or `yarn run test:view` to view the tests running in Chrome
### Linting
`make lint` currently runs linting on the majority of js files and hbs files (using `ember-template-lint`).
See `.eslintrc.js` and `.eslintignore` for specific configuration.
### Building
* `make build` builds the UI for production usage (env=production)
* `make build-ci` builds the UI for CI/test usage (env=test)
Static files are built into ./dist
#### Running Tests in Parallel
Alternatively, `ember-exam` can be used to split the tests across multiple browser instances for faster results. Most options are the same as `ember test`. To see a full list of options, run `ember exam --help`.
@ -68,12 +83,12 @@ Alternatively, `ember-exam` can be used to split the tests across multiple brows
To quickly run the tests across 4 parallel browser instances:
```sh
yarn test-parallel
make test-parallel
```
To run manually:
```sh
$ EMBER_EXAM_PARALLEL=true ember exam --split <num> --parallel
$ EMBER_EXAM_PARALLEL=true ./node_modules/.bin/ember exam --split <num> --parallel
```
More ways to split tests can be found in the [ember-exam README.md](https://github.com/trentmwillis/ember-exam/blob/master/README.md).
More ways to split tests can be found in the [ember-exam README.md](https://github.com/trentmwillis/ember-exam/blob/master/README.md).

View File

@ -1,147 +1,66 @@
import Adapter, {
REQUEST_CREATE,
REQUEST_UPDATE,
REQUEST_DELETE,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import EmberError from '@ember/error';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/acl';
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import { SLUG_KEY } from 'consul-ui/models/acl';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { PUT as HTTP_PUT } from 'consul-ui/utils/http/method';
import { OK as HTTP_OK, UNAUTHORIZED as HTTP_UNAUTHORIZED } from 'consul-ui/utils/http/status';
import makeAttrable from 'consul-ui/utils/makeAttrable';
const REQUEST_CLONE = 'cloneRecord';
export default Adapter.extend({
urlForQuery: function(query, modelName) {
requestForQuery: function(request, { dc, index }) {
// https://www.consul.io/api/acl.html#list-acls
return this.appendURL('acl/list', [], this.cleanQuery(query));
return request`
GET /v1/acl/list?${{ dc }}
${{ index }}
`;
},
urlForQueryRecord: function(query, modelName) {
// https://www.consul.io/api/acl.html#read-acl-token
if (typeof query.id === 'undefined') {
requestForQueryRecord: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('acl/info', [query.id], this.cleanQuery(query));
// https://www.consul.io/api/acl.html#read-acl-token
return request`
GET /v1/acl/info/${id}?${{ dc }}
${{ index }}
`;
},
urlForCreateRecord: function(modelName, snapshot) {
requestForCreateRecord: function(request, serialized, data) {
// https://www.consul.io/api/acl.html#create-acl-token
return this.appendURL('acl/create', [], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
return request`
PUT /v1/acl/create?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
},
urlForUpdateRecord: function(id, modelName, snapshot) {
// the id is in the payload, don't add it in here
requestForUpdateRecord: function(request, serialized, data) {
// the id is in the data, don't add it into the URL
// https://www.consul.io/api/acl.html#update-acl-token
return this.appendURL('acl/update', [], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
return request`
PUT /v1/acl/update?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
},
urlForDeleteRecord: function(id, modelName, snapshot) {
requestForDeleteRecord: function(request, serialized, data) {
// https://www.consul.io/api/acl.html#delete-acl-token
return this.appendURL('acl/destroy', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
return request`
PUT /v1/acl/destroy/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
urlForCloneRecord: function(modelName, snapshot) {
requestForCloneRecord: function(request, serialized, data) {
// https://www.consul.io/api/acl.html#clone-acl-token
return this.appendURL('acl/clone', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
return request`
PUT /v1/acl/clone/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
urlForRequest: function({ type, snapshot, requestType }) {
switch (requestType) {
case 'cloneRecord':
return this.urlForCloneRecord(type.modelName, snapshot);
}
return this._super(...arguments);
},
clone: function(store, modelClass, id, snapshot) {
const params = {
store: store,
type: modelClass,
id: id,
snapshot: snapshot,
requestType: 'cloneRecord',
};
// _requestFor is private... but these methods aren't, until they disappear..
const request = {
method: this.methodForRequest(params),
url: this.urlForRequest(params),
headers: this.headersForRequest(params),
data: this.dataForRequest(params),
};
// TODO: private..
return this._makeRequest(request);
},
dataForRequest: function(params) {
const data = this._super(...arguments);
switch (params.requestType) {
case REQUEST_UPDATE:
case REQUEST_CREATE:
return data.acl;
}
return data;
},
methodForRequest: function(params) {
switch (params.requestType) {
case REQUEST_DELETE:
case REQUEST_CREATE:
case REQUEST_CLONE:
return HTTP_PUT;
}
return this._super(...arguments);
},
isCreateRecord: function(url, method) {
return (
url.pathname ===
this.parseURL(this.urlForCreateRecord('acl', makeAttrable({ [DATACENTER_KEY]: '' }))).pathname
clone: function(store, type, id, snapshot) {
return this.request(
function(adapter, request, serialized, unserialized) {
return adapter.requestForCloneRecord(request, serialized, unserialized);
},
function(serializer, respond, serialized, unserialized) {
return serializer.respondForCreateRecord(respond, serialized, unserialized);
},
snapshot,
type.modelName
);
},
isCloneRecord: function(url, method) {
return (
url.pathname ===
this.parseURL(
this.urlForCloneRecord(
'acl',
makeAttrable({ [SLUG_KEY]: this.slugFromURL(url), [DATACENTER_KEY]: '' })
)
).pathname
);
},
isUpdateRecord: function(url, method) {
return (
url.pathname ===
this.parseURL(this.urlForUpdateRecord(null, 'acl', makeAttrable({ [DATACENTER_KEY]: '' })))
.pathname
);
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case response === true:
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
case this.isQueryRecord(url):
response = this.handleSingleResponse(url, response[0], PRIMARY_KEY, SLUG_KEY);
break;
case this.isUpdateRecord(url, method):
case this.isCreateRecord(url, method):
case this.isCloneRecord(url, method):
response = this.handleSingleResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
default:
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
} else if (status === HTTP_UNAUTHORIZED) {
const e = new EmberError();
e.code = status;
e.message = payload;
throw e;
}
return this._super(status, headers, response, requestData);
},
});

View File

@ -1,182 +1,44 @@
import Adapter from 'ember-data/adapters/rest';
import { AbortError } from 'ember-data/adapters/errors';
import Adapter from './http';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
import URL from 'url';
import createURL from 'consul-ui/utils/createURL';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { HEADERS_SYMBOL as HTTP_HEADERS_SYMBOL } from 'consul-ui/utils/http/consul';
export const REQUEST_CREATE = 'createRecord';
export const REQUEST_READ = 'queryRecord';
export const REQUEST_UPDATE = 'updateRecord';
export const REQUEST_DELETE = 'deleteRecord';
// export const REQUEST_READ_MULTIPLE = 'query';
export const DATACENTER_QUERY_PARAM = 'dc';
export default Adapter.extend({
namespace: 'v1',
repo: service('settings'),
client: service('client/http'),
manageConnection: function(options) {
const client = get(this, 'client');
const complete = options.complete;
const beforeSend = options.beforeSend;
options.beforeSend = function(xhr) {
if (typeof beforeSend === 'function') {
beforeSend(...arguments);
}
options.id = client.request(options, xhr);
};
options.complete = function(xhr, textStatus) {
client.complete(options.id);
if (typeof complete === 'function') {
complete(...arguments);
}
};
return options;
},
_ajaxRequest: function(options) {
return this._super(this.manageConnection(options));
},
queryRecord: function() {
return this._super(...arguments).catch(function(e) {
if (e instanceof AbortError) {
e.errors[0].status = '0';
}
throw e;
});
},
query: function() {
return this._super(...arguments).catch(function(e) {
if (e instanceof AbortError) {
e.errors[0].status = '0';
}
throw e;
});
},
headersForRequest: function(params) {
return {
...this.get('repo').findHeaders(),
...this._super(...arguments),
};
},
handleResponse: function(status, headers, response, requestData) {
// The ember-data RESTAdapter drops the headers after this call,
// and there is no where else to get to these
// save them to response[HTTP_HEADERS_SYMBOL] for the moment
// so we can save them as meta in the serializer...
if (
(typeof response == 'object' && response.constructor == Object) ||
Array.isArray(response)
) {
// lowercase everything incase we get browser inconsistencies
const lower = {};
Object.keys(headers).forEach(function(key) {
lower[key.toLowerCase()] = headers[key];
// TODO: kinda protected for the moment
// decide where this should go either read/write from http
// should somehow use this or vice versa
request: function(req, resp, obj, modelName) {
const client = this.client;
const store = this.store;
const adapter = this;
let unserialized, serialized;
const serializer = store.serializerFor(modelName);
// workable way to decide whether this is a snapshot
// Snapshot is private so we can't do instanceof here
if (obj.constructor.name === 'Snapshot') {
unserialized = obj.attributes();
serialized = serializer.serialize(obj, {});
} else {
unserialized = obj;
serialized = unserialized;
}
return client
.request(function(request) {
return req(adapter, request, serialized, unserialized);
})
.catch(function(e) {
return adapter.error(e);
})
.then(function(respond) {
// TODO: When HTTPAdapter:responder changes, this will also need to change
return resp(serializer, respond, serialized, unserialized);
});
response[HTTP_HEADERS_SYMBOL] = lower;
}
return this._super(status, headers, response, requestData);
},
handleBooleanResponse: function(url, response, primary, slug) {
return {
// consider a check for a boolean, also for future me,
// response[slug] // this will forever be null, response should be boolean
[primary]: this.uidForURL(url /* response[slug]*/),
};
},
// could always consider an extra 'dc' arg on the end here?
handleSingleResponse: function(url, response, primary, slug, _dc) {
const dc =
typeof _dc !== 'undefined' ? _dc : url.searchParams.get(DATACENTER_QUERY_PARAM) || '';
return {
...response,
...{
[DATACENTER_KEY]: dc,
[primary]: this.uidForURL(url, response[slug]),
},
};
},
handleBatchResponse: function(url, response, primary, slug) {
const dc = url.searchParams.get(DATACENTER_QUERY_PARAM) || '';
return response.map((item, i, arr) => {
return this.handleSingleResponse(url, item, primary, slug, dc);
});
},
cleanQuery: function(_query) {
if (typeof _query.id !== 'undefined') {
delete _query.id;
}
const query = { ..._query };
if (typeof query.separator !== 'undefined') {
delete query.separator;
}
if (typeof query.index !== 'undefined') {
delete query.index;
}
delete _query[DATACENTER_QUERY_PARAM];
return query;
},
isUpdateRecord: function(url, method) {
return false;
},
isCreateRecord: function(url, method) {
return false;
},
isQueryRecord: function(url, method) {
// this is ONLY if ALL api's using it
// follow the 'last part of the url is the id' rule
const pathname = url.pathname
.split('/') // unslashify
// remove the last
.slice(0, -1)
// add and empty to ensure a trailing slash
.concat([''])
// slashify
.join('/');
// compare with empty id against empty id
return pathname === this.parseURL(this.urlForQueryRecord({ id: '' })).pathname;
},
getHost: function() {
return this.host || `${location.protocol}//${location.host}`;
},
slugFromURL: function(url, decode = decodeURIComponent) {
// follow the 'last part of the url is the id' rule
return decode(url.pathname.split('/').pop());
},
parseURL: function(str) {
return new URL(str, this.getHost());
},
uidForURL: function(url, _slug = '', hash = JSON.stringify) {
const dc = url.searchParams.get(DATACENTER_QUERY_PARAM) || '';
const slug = _slug === '' ? this.slugFromURL(url) : _slug;
if (dc.length < 1) {
throw new Error('Unable to create unique id, missing datacenter');
}
if (slug.length < 1) {
throw new Error('Unable to create unique id, missing slug');
}
// TODO: we could use a URL here? They are unique AND useful
// but probably slower to create?
return hash([dc, slug]);
},
// appendURL in turn calls createURL
// createURL ensures that all `parts` are URL encoded
// and all `query` values are URL encoded
// `this.buildURL()` with no arguments will give us `${host}/${namespace}`
// `path` is the user configurable 'urlsafe' string to append on `buildURL`
// `parts` is an array of possibly non 'urlsafe parts' to be encoded and
// appended onto the url
// `query` will populate the query string. Again the values of which will be
// url encoded
appendURL: function(path, parts = [], query = {}) {
// path can be a string or an array of parts that will be slash joined
return createURL([this.buildURL()].concat(path), parts, query);
// TODO: Potentially add specific serializer errors here
// .catch(function(e) {
// return Promise.reject(e);
// });
},
});

View File

@ -1,23 +1,10 @@
import ApplicationAdapter from './application';
import Adapter from './application';
export default Adapter.extend({
requestForQuery: function(request, { dc, index }) {
return request`
GET /v1/coordinate/nodes?${{ dc }}
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/coordinate';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
export default ApplicationAdapter.extend({
urlForQuery: function(query, modelName) {
// https://www.consul.io/api/coordinate.html#read-lan-coordinates-for-all-nodes
return this.appendURL('coordinate/nodes', [], this.cleanQuery(query));
},
isQueryRecord: function(url) {
return true;
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
return this._super(status, headers, response, requestData);
${{ index }}
`;
},
});

View File

@ -1,7 +1,9 @@
import Adapter from './application';
export default Adapter.extend({
urlForFindAll: function() {
return this.appendURL('catalog/datacenters');
requestForFindAll: function(request) {
return request`
GET /v1/catalog/datacenters
`;
},
});

117
ui-v2/app/adapters/http.js Normal file
View File

@ -0,0 +1,117 @@
import Adapter from 'ember-data/adapter';
import {
AbortError,
TimeoutError,
ServerError,
UnauthorizedError,
ForbiddenError,
NotFoundError,
ConflictError,
InvalidError,
AdapterError,
} from 'ember-data/adapters/errors';
// TODO: This is a little skeleton cb function
// is to be replaced soon with something slightly more involved
const responder = function(response) {
return response;
};
const read = function(adapter, serializer, client, type, query) {
return client
.request(function(request) {
return adapter[`requestFor${type}`](request, query);
})
.catch(function(e) {
return adapter.error(e);
})
.then(function(response) {
return serializer[`respondFor${type}`](responder(response), query);
});
// TODO: Potentially add specific serializer errors here
// .catch(function(e) {
// return Promise.reject(e);
// });
};
const write = function(adapter, serializer, client, type, snapshot) {
const unserialized = snapshot.attributes();
const serialized = serializer.serialize(snapshot, {});
return client
.request(function(request) {
return adapter[`requestFor${type}`](request, serialized, unserialized);
})
.catch(function(e) {
return adapter.error(e);
})
.then(function(response) {
return serializer[`respondFor${type}`](responder(response), serialized, unserialized);
});
// TODO: Potentially add specific serializer errors here
// .catch(function(e) {
// return Promise.reject(e);
// });
};
export default Adapter.extend({
error: function(err) {
const errors = [
{
status: `${err.statusCode}`,
title: 'The backend responded with an error',
detail: err.message,
},
];
let error;
const detailedMessage = '';
try {
switch (err.statusCode) {
case 0:
error = new AbortError();
break;
case 401:
error = new UnauthorizedError(errors, detailedMessage);
break;
case 403:
error = new ForbiddenError(errors, detailedMessage);
break;
case 404:
error = new NotFoundError(errors, detailedMessage);
break;
case 408:
error = new TimeoutError();
break;
case 409:
error = new ConflictError(errors, detailedMessage);
break;
case 422:
error = new InvalidError(errors); //payload.errors
break;
default:
if (err.statusCode >= 500) {
error = new ServerError(errors, detailedMessage);
} else {
error = new AdapterError(errors, detailedMessage);
}
}
} catch (e) {
error = e;
}
throw error;
},
query: function(store, type, query) {
return read(this, store.serializerFor(type.modelName), this.client, 'Query', query);
},
queryRecord: function(store, type, query) {
return read(this, store.serializerFor(type.modelName), this.client, 'QueryRecord', query);
},
findAll: function(store, type) {
return read(this, store.serializerFor(type.modelName), this.client, 'FindAll');
},
createRecord: function(store, type, snapshot) {
return write(this, store.serializerFor(type.modelName), this.client, 'CreateRecord', snapshot);
},
updateRecord: function(store, type, snapshot) {
return write(this, store.serializerFor(type.modelName), this.client, 'UpdateRecord', snapshot);
},
deleteRecord: function(store, type, snapshot) {
return write(this, store.serializerFor(type.modelName), this.client, 'DeleteRecord', snapshot);
},
});

View File

@ -1,80 +1,44 @@
import Adapter, {
REQUEST_CREATE,
REQUEST_UPDATE,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/intention';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
import { POST as HTTP_POST } from 'consul-ui/utils/http/method';
import makeAttrable from 'consul-ui/utils/makeAttrable';
import { SLUG_KEY } from 'consul-ui/models/intention';
export default Adapter.extend({
urlForQuery: function(query, modelName) {
return this.appendURL('connect/intentions', [], this.cleanQuery(query));
requestForQuery: function(request, { dc, index, id }) {
return request`
GET /v1/connect/intentions?${{ dc }}
${{ index }}
`;
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
requestForQueryRecord: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('connect/intentions', [query.id], this.cleanQuery(query));
return request`
GET /v1/connect/intentions/${id}?${{ dc }}
${{ index }}
`;
},
urlForCreateRecord: function(modelName, snapshot) {
return this.appendURL('connect/intentions', [], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
requestForCreateRecord: function(request, serialized, data) {
// TODO: need to make sure we remove dc
return request`
POST /v1/connect/intentions?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
},
urlForUpdateRecord: function(id, modelName, snapshot) {
return this.appendURL('connect/intentions', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
requestForUpdateRecord: function(request, serialized, data) {
return request`
PUT /v1/connect/intentions/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
},
urlForDeleteRecord: function(id, modelName, snapshot) {
return this.appendURL('connect/intentions', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
},
isUpdateRecord: function(url, method) {
return (
url.pathname ===
this.parseURL(
this.urlForUpdateRecord(null, 'intention', makeAttrable({ [DATACENTER_KEY]: '' }))
).pathname
);
},
isCreateRecord: function(url, method) {
return (
method.toUpperCase() === HTTP_POST &&
url.pathname ===
this.parseURL(this.urlForCreateRecord('intention', makeAttrable({ [DATACENTER_KEY]: '' })))
.pathname
);
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case this.isQueryRecord(url, method):
case this.isUpdateRecord(url, method):
case this.isCreateRecord(url, method):
// TODO: We just need to upgrade this (^^ sorry linter) entire API to
// use a full request-like object
response = this.handleSingleResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
default:
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);
},
dataForRequest: function(params) {
const data = this._super(...arguments);
switch (params.requestType) {
case REQUEST_UPDATE:
case REQUEST_CREATE:
return data.intention;
}
return data;
requestForDeleteRecord: function(request, serialized, data) {
return request`
DELETE /v1/connect/intentions/${data[SLUG_KEY]}?${{
[API_DATACENTER_KEY]: data[DATACENTER_KEY],
}}
`;
},
});

View File

@ -1,150 +1,61 @@
import Adapter, {
REQUEST_CREATE,
REQUEST_UPDATE,
REQUEST_DELETE,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import isFolder from 'consul-ui/utils/isFolder';
import injectableRequestToJQueryAjaxHash from 'consul-ui/utils/injectableRequestToJQueryAjaxHash';
import { typeOf } from '@ember/utils';
import { get } from '@ember/object';
import { inject as service } from '@ember/service';
import keyToArray from 'consul-ui/utils/keyToArray';
import removeNull from 'consul-ui/utils/remove-null';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/kv';
import { SLUG_KEY } from 'consul-ui/models/kv';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { PUT as HTTP_PUT, DELETE as HTTP_DELETE } from 'consul-ui/utils/http/method';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
const API_KEYS_KEY = 'keys';
const stringify = function(obj) {
if (typeOf(obj) === 'string') {
return obj;
}
return JSON.stringify(obj);
};
export default Adapter.extend({
// There is no code path that can avoid the payload of a PUT request from
// going via JSON.stringify.
// Therefore a string payload of 'foobar' will always be encoded to '"foobar"'
//
// This means we have no other choice but rewriting the entire codepath or
// overwriting the private `_requestToJQueryAjaxHash` method
//
// The `injectableRequestToJQueryAjaxHash` function makes the JSON object
// injectable, meaning we can copy letter for letter the sourcecode of
// `_requestToJQueryAjaxHash`, which means we can compare it with the original
// private method within a test (`tests/unit/utils/injectableRequestToJQueryAjaxHash.js`).
// This means, if `_requestToJQueryAjaxHash` changes between Ember versions
// we will know about it
requestForQuery: function(request, { dc, index, id, separator }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return request`
GET /v1/kv/${keyToArray(id)}?${{ [API_KEYS_KEY]: null, dc, separator }}
_requestToJQueryAjaxHash: injectableRequestToJQueryAjaxHash({
stringify: stringify,
}),
decoder: service('atob'),
urlForQuery: function(query, modelName) {
if (typeof query.id === 'undefined') {
${{ index }}
`;
},
requestForQueryRecord: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
// append keys here otherwise query.keys will add an '='
return this.appendURL('kv', keyToArray(query.id), {
...{
[API_KEYS_KEY]: null,
},
...this.cleanQuery(query),
});
return request`
GET /v1/kv/${keyToArray(id)}?${{ dc }}
${{ index }}
`;
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
throw new Error('You must specify an id');
// TODO: Should we replace text/plain here with x-www-form-encoded?
// See https://github.com/hashicorp/consul/issues/3804
requestForCreateRecord: function(request, serialized, data) {
return request`
PUT /v1/kv/${keyToArray(data[SLUG_KEY])}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
Content-Type: text/plain; charset=utf-8
${serialized}
`;
},
requestForUpdateRecord: function(request, serialized, data) {
return request`
PUT /v1/kv/${keyToArray(data[SLUG_KEY])}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
Content-Type: text/plain; charset=utf-8
${serialized}
`;
},
requestForDeleteRecord: function(request, serialized, data) {
let recurse;
if (isFolder(data[SLUG_KEY])) {
recurse = null;
}
return this.appendURL('kv', keyToArray(query.id), this.cleanQuery(query));
},
urlForCreateRecord: function(modelName, snapshot) {
return this.appendURL('kv', keyToArray(snapshot.attr(SLUG_KEY)), {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
},
urlForUpdateRecord: function(id, modelName, snapshot) {
return this.appendURL('kv', keyToArray(snapshot.attr(SLUG_KEY)), {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
},
urlForDeleteRecord: function(id, modelName, snapshot) {
const query = {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
};
if (isFolder(snapshot.attr(SLUG_KEY))) {
query.recurse = null;
}
return this.appendURL('kv', keyToArray(snapshot.attr(SLUG_KEY)), query);
},
slugFromURL: function(url) {
// keys don't follow the 'last part of the url' rule as they contain slashes
return decodeURIComponent(
url.pathname
.split('/')
.splice(3)
.join('/')
);
},
isQueryRecord: function(url, method) {
return !url.searchParams.has(API_KEYS_KEY);
},
handleBatchResponse: function(url, response, primary, slug) {
const dc = url.searchParams.get(API_DATACENTER_KEY) || '';
return response.map((item, i, arr) => {
return {
[DATACENTER_KEY]: dc,
[PRIMARY_KEY]: this.uidForURL(url, item),
[SLUG_KEY]: item,
};
});
},
// handleSingleResponse: function(url, response, primary, slug) {
// return this._super(url, removeNull(response[0]), PRIMARY_KEY, SLUG_KEY);
// },
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case response === true:
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
case this.isQueryRecord(url, method):
response = this.handleSingleResponse(url, removeNull(response[0]), PRIMARY_KEY, SLUG_KEY);
break;
default:
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);
},
dataForRequest: function(params) {
const data = this._super(...arguments);
let value = '';
switch (params.requestType) {
case REQUEST_UPDATE:
case REQUEST_CREATE:
value = data.kv.Value;
if (typeof value === 'string') {
return get(this, 'decoder').execute(value);
}
return null;
}
return data;
},
methodForRequest: function(params) {
switch (params.requestType) {
case REQUEST_DELETE:
return HTTP_DELETE;
case REQUEST_CREATE:
return HTTP_PUT;
}
return this._super(...arguments);
return request`
DELETE /v1/kv/${keyToArray(data[SLUG_KEY])}?${{
[API_DATACENTER_KEY]: data[DATACENTER_KEY],
recurse,
}}
`;
},
});

View File

@ -1,100 +1,37 @@
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/node';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
// TODO: Looks like ID just isn't used at all
// consider just using .Node for the SLUG_KEY
const fillSlug = function(item) {
if (item[SLUG_KEY] === '') {
item[SLUG_KEY] = item['Node'];
}
return item;
};
import Adapter from './application';
export default Adapter.extend({
urlForQuery: function(query, modelName) {
return this.appendURL('internal/ui/nodes', [], this.cleanQuery(query));
requestForQuery: function(request, { dc, index, id }) {
return request`
GET /v1/internal/ui/nodes?${{ dc }}
${{ index }}
`;
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
requestForQueryRecord: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('internal/ui/node', [query.id], this.cleanQuery(query));
return request`
GET /v1/internal/ui/node/${id}?${{ dc }}
${{ index }}
`;
},
urlForRequest: function({ type, snapshot, requestType }) {
switch (requestType) {
case 'queryLeader':
return this.urlForQueryLeader(snapshot, type.modelName);
}
return this._super(...arguments);
requestForQueryLeader: function(request, { dc }) {
return request`
GET /v1/status/leader?${{ dc }}
`;
},
urlForQueryLeader: function(query, modelName) {
// https://www.consul.io/api/status.html#get-raft-leader
return this.appendURL('status/leader', [], this.cleanQuery(query));
},
isQueryLeader: function(url, method) {
return url.pathname === this.parseURL(this.urlForQueryLeader({})).pathname;
},
queryLeader: function(store, modelClass, id, snapshot) {
const params = {
store: store,
type: modelClass,
id: id,
snapshot: snapshot,
requestType: 'queryLeader',
};
// _requestFor is private... but these methods aren't, until they disappear..
const request = {
method: this.methodForRequest(params),
url: this.urlForRequest(params),
headers: this.headersForRequest(params),
data: this.dataForRequest(params),
};
// TODO: private..
return this._makeRequest(request);
},
handleBatchResponse: function(url, response, primary, slug) {
const dc = url.searchParams.get(API_DATACENTER_KEY) || '';
return response.map((item, i, arr) => {
// this can go in the serializer
item = fillSlug(item);
// this could be replaced by handleSingleResponse
// maybe perf test first although even polyfilled searchParams should be super fast
return {
...item,
...{
[DATACENTER_KEY]: dc,
[PRIMARY_KEY]: this.uidForURL(url, item[SLUG_KEY]),
},
};
});
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
let temp, port, address;
switch (true) {
case this.isQueryLeader(url, method):
// This response is just an ip:port like `"10.0.0.1:8000"`
// split it and make it look like a `C`onsul.`R`esponse
// popping off the end for ports should cover us for IPv6 addresses
// as we should always get a `address:port` or `[a:dd:re:ss]:port` combo
temp = response.split(':');
port = temp.pop();
address = temp.join(':');
response = {
Address: address,
Port: port,
};
break;
case this.isQueryRecord(url, method):
response = this.handleSingleResponse(url, fillSlug(response), PRIMARY_KEY, SLUG_KEY);
break;
default:
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);
queryLeader: function(store, type, id, snapshot) {
return this.request(
function(adapter, request, serialized, unserialized) {
return adapter.requestForQueryLeader(request, serialized, unserialized);
},
function(serializer, respond, serialized, unserialized) {
return serializer.respondForQueryLeader(respond, serialized, unserialized);
},
snapshot,
type.modelName
);
},
});

View File

@ -1,73 +1,43 @@
import Adapter, {
REQUEST_CREATE,
REQUEST_UPDATE,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/policy';
import { SLUG_KEY } from 'consul-ui/models/policy';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
import { PUT as HTTP_PUT } from 'consul-ui/utils/http/method';
export default Adapter.extend({
urlForQuery: function(query, modelName) {
return this.appendURL('acl/policies', [], this.cleanQuery(query));
requestForQuery: function(request, { dc, index, id }) {
return request`
GET /v1/acl/policies?${{ dc }}
${{ index }}
`;
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
requestForQueryRecord: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('acl/policy', [query.id], this.cleanQuery(query));
return request`
GET /v1/acl/policy/${id}?${{ dc }}
${{ index }}
`;
},
urlForCreateRecord: function(modelName, snapshot) {
return this.appendURL('acl/policy', [], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
requestForCreateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/policy?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
},
urlForUpdateRecord: function(id, modelName, snapshot) {
return this.appendURL('acl/policy', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
requestForUpdateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/policy/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
},
urlForDeleteRecord: function(id, modelName, snapshot) {
return this.appendURL('acl/policy', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
},
urlForTranslateRecord: function(modelName, snapshot) {
return this.appendURL('acl/policy/translate', [], {});
},
dataForRequest: function(params) {
const data = this._super(...arguments);
switch (params.requestType) {
case REQUEST_UPDATE:
case REQUEST_CREATE:
return data.policy;
}
return data;
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case response === true:
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
case Array.isArray(response):
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
default:
response = this.handleSingleResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);
},
methodForRequest: function(params) {
switch (params.requestType) {
case REQUEST_CREATE:
return HTTP_PUT;
}
return this._super(...arguments);
requestForDeleteRecord: function(request, serialized, data) {
return request`
DELETE /v1/acl/policy/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
});

View File

@ -1,20 +1,13 @@
import Adapter from './application';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/proxy';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
export default Adapter.extend({
urlForQuery: function(query, modelName) {
if (typeof query.id === 'undefined') {
requestForQuery: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
// https://www.consul.io/api/catalog.html#list-nodes-for-connect-capable-service
return this.appendURL('catalog/connect', [query.id], this.cleanQuery(query));
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
return this._super(status, headers, response, requestData);
return request`
GET /v1/catalog/connect/${id}?${{ dc }}
${{ index }}
`;
},
});

View File

@ -1,72 +1,43 @@
import Adapter, {
REQUEST_CREATE,
REQUEST_UPDATE,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/role';
import { SLUG_KEY } from 'consul-ui/models/role';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
import { PUT as HTTP_PUT } from 'consul-ui/utils/http/method';
import WithPolicies from 'consul-ui/mixins/policy/as-many';
export default Adapter.extend({
requestForQuery: function(request, { dc, index, id }) {
return request`
GET /v1/acl/roles?${{ dc }}
export default Adapter.extend(WithPolicies, {
urlForQuery: function(query, modelName) {
return this.appendURL('acl/roles', [], this.cleanQuery(query));
${{ index }}
`;
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
requestForQueryRecord: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('acl/role', [query.id], this.cleanQuery(query));
return request`
GET /v1/acl/role/${id}?${{ dc }}
${{ index }}
`;
},
urlForCreateRecord: function(modelName, snapshot) {
return this.appendURL('acl/role', [], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
requestForCreateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/role?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
},
urlForUpdateRecord: function(id, modelName, snapshot) {
return this.appendURL('acl/role', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
requestForUpdateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/role/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
},
urlForDeleteRecord: function(id, modelName, snapshot) {
return this.appendURL('acl/role', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case response === true:
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
case Array.isArray(response):
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
default:
response = this.handleSingleResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);
},
methodForRequest: function(params) {
switch (params.requestType) {
case REQUEST_CREATE:
return HTTP_PUT;
}
return this._super(...arguments);
},
dataForRequest: function(params) {
const data = this._super(...arguments);
switch (params.requestType) {
case REQUEST_UPDATE:
case REQUEST_CREATE:
return data.role;
}
return data;
requestForDeleteRecord: function(request, serialized, data) {
return request`
DELETE /v1/acl/role/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
});

View File

@ -1,31 +1,20 @@
import Adapter from './application';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/service';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
const URL_PREFIX_SINGLE = 'health/service';
const URL_PREFIX_MULTIPLE = 'internal/ui/services';
export default Adapter.extend({
urlForQuery: function(query, modelName) {
return this.appendURL(URL_PREFIX_MULTIPLE, [], this.cleanQuery(query));
requestForQuery: function(request, { dc, index }) {
return request`
GET /v1/internal/ui/services?${{ dc }}
${{ index }}
`;
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
requestForQueryRecord: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL(URL_PREFIX_SINGLE, [query.id], this.cleanQuery(query));
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case this.isQueryRecord(url, method):
response = this.handleSingleResponse(url, { Nodes: response }, PRIMARY_KEY, SLUG_KEY);
break;
default:
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);
return request`
GET /v1/health/service/${id}?${{ dc }}
${{ index }}
`;
},
});

View File

@ -1,55 +1,31 @@
import Adapter, {
REQUEST_DELETE,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/session';
import { PUT as HTTP_PUT } from 'consul-ui/utils/http/method';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
import { SLUG_KEY } from 'consul-ui/models/session';
export default Adapter.extend({
urlForQuery: function(query, modelName) {
if (typeof query.id === 'undefined') {
requestForQuery: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('session/node', [query.id], this.cleanQuery(query));
return request`
GET /v1/session/node/${id}?${{ dc }}
${{ index }}
`;
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
requestForQueryRecord: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('session/info', [query.id], this.cleanQuery(query));
return request`
GET /v1/session/info/${id}?${{ dc }}
${{ index }}
`;
},
urlForDeleteRecord: function(id, modelName, snapshot) {
const query = {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
};
return this.appendURL('session/destroy', [snapshot.attr(SLUG_KEY)], query);
},
methodForRequest: function(params) {
switch (params.requestType) {
case REQUEST_DELETE:
return HTTP_PUT;
}
return this._super(...arguments);
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
const method = requestData.method;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case response === true:
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
case this.isQueryRecord(url, method):
response = this.handleSingleResponse(url, response[0], PRIMARY_KEY, SLUG_KEY);
break;
default:
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);
requestForDeleteRecord: function(request, serialized, data) {
return request`
PUT /v1/session/destroy/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
});

View File

@ -1,185 +1,110 @@
import Adapter, { DATACENTER_QUERY_PARAM as API_DATACENTER_KEY } from './application';
import { inject as service } from '@ember/service';
import Adapter, {
REQUEST_CREATE,
REQUEST_UPDATE,
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY,
} from './application';
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/token';
import { SLUG_KEY } from 'consul-ui/models/token';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { OK as HTTP_OK } from 'consul-ui/utils/http/status';
import { PUT as HTTP_PUT } from 'consul-ui/utils/http/method';
import WithPolicies from 'consul-ui/mixins/policy/as-many';
import WithRoles from 'consul-ui/mixins/role/as-many';
import { get } from '@ember/object';
const REQUEST_CLONE = 'cloneRecord';
const REQUEST_SELF = 'querySelf';
export default Adapter.extend(WithRoles, WithPolicies, {
export default Adapter.extend({
store: service('store'),
cleanQuery: function(_query) {
const query = this._super(...arguments);
// TODO: Make sure policy is being passed through
delete _query.policy;
// take off the secret for /self
delete query.secret;
return query;
requestForQuery: function(request, { dc, index, role, policy }) {
return request`
GET /v1/acl/tokens?${{ role, policy, dc }}
${{ index }}
`;
},
urlForQuery: function(query, modelName) {
return this.appendURL('acl/tokens', [], this.cleanQuery(query));
},
urlForQueryRecord: function(query, modelName) {
if (typeof query.id === 'undefined') {
requestForQueryRecord: function(request, { dc, index, id }) {
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this.appendURL('acl/token', [query.id], this.cleanQuery(query));
return request`
GET /v1/acl/token/${id}?${{ dc }}
${{ index }}
`;
},
urlForQuerySelf: function(query, modelName) {
return this.appendURL('acl/token/self', [], this.cleanQuery(query));
requestForCreateRecord: function(request, serialized, data) {
return request`
PUT /v1/acl/token?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
urlForCreateRecord: function(modelName, snapshot) {
return this.appendURL('acl/token', [], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
},
urlForUpdateRecord: function(id, modelName, snapshot) {
requestForUpdateRecord: function(request, serialized, data) {
// TODO: here we check data['Rules'] not serialized['Rules']
// data.Rules is not undefined, and serialized.Rules is not null
// revisit this at some point we should probably use serialized here
// If a token has Rules, use the old API
if (typeof snapshot.attr('Rules') !== 'undefined') {
return this.appendURL('acl/update', [], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
if (typeof data['Rules'] !== 'undefined') {
// https://www.consul.io/api/acl/legacy.html#update-acl-token
return request`
PUT /v1/acl/update?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
}
return this.appendURL('acl/token', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
return request`
PUT /v1/acl/token/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
${serialized}
`;
},
urlForDeleteRecord: function(id, modelName, snapshot) {
return this.appendURL('acl/token', [snapshot.attr(SLUG_KEY)], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
requestForDeleteRecord: function(request, serialized, data) {
return request`
DELETE /v1/acl/token/${data[SLUG_KEY]}?${{ [API_DATACENTER_KEY]: data[DATACENTER_KEY] }}
`;
},
urlForRequest: function({ type, snapshot, requestType }) {
switch (requestType) {
case 'cloneRecord':
return this.urlForCloneRecord(type.modelName, snapshot);
case 'querySelf':
return this.urlForQuerySelf(snapshot, type.modelName);
requestForSelf: function(request, serialized, { dc, index, secret }) {
// TODO: Change here and elsewhere to use Authorization Bearer Token
// https://github.com/hashicorp/consul/pull/4502
return request`
GET /v1/acl/token/self?${{ dc }}
X-Consul-Token: ${secret}
${{ index }}
`;
},
requestForCloneRecord: function(request, serialized, unserialized) {
// this uses snapshots
const id = unserialized[SLUG_KEY];
const dc = unserialized[DATACENTER_KEY];
if (typeof id === 'undefined') {
throw new Error('You must specify an id');
}
return this._super(...arguments);
return request`
PUT /v1/acl/token/${id}/clone?${{ [API_DATACENTER_KEY]: dc }}
`;
},
urlForCloneRecord: function(modelName, snapshot) {
return this.appendURL('acl/token', [snapshot.attr(SLUG_KEY), 'clone'], {
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY),
});
// TODO: self doesn't get passed a snapshot right now
// ideally it would just for consistency
// thing is its probably not the same shape as a 'Token',
// plus we can't create Snapshots as they are private, see services/store.js
self: function(store, type, id, unserialized) {
return this.request(
function(adapter, request, serialized, unserialized) {
return adapter.requestForSelf(request, serialized, unserialized);
},
function(serializer, respond, serialized, unserialized) {
return serializer.respondForQueryRecord(respond, serialized, unserialized);
},
unserialized,
type.modelName
);
},
self: function(store, modelClass, snapshot) {
const params = {
store: store,
type: modelClass,
snapshot: snapshot,
requestType: 'querySelf',
};
// _requestFor is private... but these methods aren't, until they disappear..
const request = {
method: this.methodForRequest(params),
url: this.urlForRequest(params),
headers: this.headersForRequest(params),
data: this.dataForRequest(params),
};
// TODO: private..
return this._makeRequest(request);
},
clone: function(store, modelClass, id, snapshot) {
const params = {
store: store,
type: modelClass,
id: id,
snapshot: snapshot,
requestType: 'cloneRecord',
};
// _requestFor is private... but these methods aren't, until they disappear..
const request = {
method: this.methodForRequest(params),
url: this.urlForRequest(params),
headers: this.headersForRequest(params),
data: this.dataForRequest(params),
};
// TODO: private..
return this._makeRequest(request);
},
handleSingleResponse: function(url, response, primary, slug) {
// Convert an old style update response to a new style
if (typeof response['ID'] !== 'undefined') {
const item = get(this, 'store')
.peekAll('token')
.findBy('SecretID', response['ID']);
if (item) {
response['SecretID'] = response['ID'];
response['AccessorID'] = get(item, 'AccessorID');
}
}
return this._super(url, response, primary, slug);
},
handleResponse: function(status, headers, payload, requestData) {
let response = payload;
if (status === HTTP_OK) {
const url = this.parseURL(requestData.url);
switch (true) {
case response === true:
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
case Array.isArray(response):
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY);
break;
default:
response = this.handleSingleResponse(url, response, PRIMARY_KEY, SLUG_KEY);
}
}
return this._super(status, headers, response, requestData);
},
methodForRequest: function(params) {
switch (params.requestType) {
case REQUEST_CLONE:
case REQUEST_CREATE:
return HTTP_PUT;
}
return this._super(...arguments);
},
headersForRequest: function(params) {
switch (params.requestType) {
case REQUEST_SELF:
return {
'X-Consul-Token': params.snapshot.secret,
};
}
return this._super(...arguments);
},
dataForRequest: function(params) {
let data = this._super(...arguments);
switch (params.requestType) {
case REQUEST_UPDATE:
// If a token has Rules, use the old API
if (typeof data.token['Rules'] !== 'undefined') {
data.token['ID'] = data.token['SecretID'];
data.token['Name'] = data.token['Description'];
}
// falls through
case REQUEST_CREATE:
data = data.token;
break;
case REQUEST_SELF:
return {};
case REQUEST_CLONE:
data = {};
break;
}
// make sure we never send the SecretID
if (data && typeof data['SecretID'] !== 'undefined') {
delete data['SecretID'];
}
return data;
clone: function(store, type, id, snapshot) {
return this.request(
function(adapter, request, serialized, unserialized) {
return adapter.requestForCloneRecord(request, serialized, unserialized);
},
function(serializer, respond, serialized, unserialized) {
// here we just have to pass through the dc (like when querying)
// eventually the id is created with this dc value and the id talen from the
// json response of `acls/token/*/clone`
return serializer.respondForQueryRecord(respond, {
[API_DATACENTER_KEY]: unserialized[SLUG_KEY],
});
},
snapshot,
type.modelName
);
},
});

View File

@ -1,6 +1,5 @@
import Component from '@ember/component';
import SlotsMixin from 'block-slots';
import { get } from '@ember/object';
import { inject as service } from '@ember/service';
import templatize from 'consul-ui/utils/templatize';
export default Component.extend(SlotsMixin, {
@ -13,9 +12,9 @@ export default Component.extend(SlotsMixin, {
didReceiveAttrs: function() {
this._super(...arguments);
// right now only manually added classes are hoisted to <html>
const $root = get(this, 'dom').root();
let cls = get(this, 'class') || '';
if (get(this, 'loading')) {
const $root = this.dom.root();
let cls = this['class'] || '';
if (this.loading) {
cls += ' loading';
} else {
$root.classList.remove(...templatize(['loading']));
@ -39,9 +38,9 @@ export default Component.extend(SlotsMixin, {
},
didDestroyElement: function() {
this._super(...arguments);
const cls = get(this, 'class') + ' loading';
const cls = this['class'] + ' loading';
if (cls) {
const $root = get(this, 'dom').root();
const $root = this.dom.root();
$root.classList.remove(...templatize(cls.split(' ')));
}
},

View File

@ -8,7 +8,7 @@ export default Component.extend(WithListeners, SlotsMixin, {
didReceiveAttrs: function() {
this._super(...arguments);
this.removeListeners();
const dispatcher = get(this, 'dispatcher');
const dispatcher = this.dispatcher;
if (dispatcher) {
this.listen(dispatcher, 'change', e => {
set(this, 'items', e.target.data);

View File

@ -23,15 +23,15 @@ export default Component.extend(SlotsMixin, WithListeners, {
init: function() {
this._super(...arguments);
this.searchable = get(this, 'container').searchable(get(this, 'type'));
this.form = get(this, 'formContainer').form(get(this, 'type'));
this.form.clear({ Datacenter: get(this, 'dc') });
this.searchable = this.container.searchable(this.type);
this.form = this.formContainer.form(this.type);
this.form.clear({ Datacenter: this.dc });
},
options: computed('selectedOptions.[]', 'allOptions.[]', function() {
// It's not massively important here that we are defaulting `items` and
// losing reference as its just to figure out the diff
let options = get(this, 'allOptions') || [];
const items = get(this, 'selectedOptions') || [];
let options = this.allOptions || [];
const items = this.selectedOptions || [];
if (get(items, 'length') > 0) {
// find a proper ember-data diff
options = options.filter(item => !items.findBy('ID', get(item, 'ID')));
@ -52,17 +52,17 @@ export default Component.extend(SlotsMixin, WithListeners, {
});
},
reset: function() {
get(this, 'form').clear({ Datacenter: get(this, 'dc') });
this.form.clear({ Datacenter: this.dc });
},
open: function() {
if (!get(this, 'allOptions.closed')) {
set(this, 'allOptions', get(this, 'repo').findAllByDatacenter(get(this, 'dc')));
set(this, 'allOptions', this.repo.findAllByDatacenter(this.dc));
}
},
save: function(item, items, success = function() {}) {
// Specifically this saves an 'new' option/child
// and then adds it to the selectedOptions, not options
const repo = get(this, 'repo');
const repo = this.repo;
set(item, 'CreateTime', new Date().getTime());
// TODO: temporary async
// this should be `set(this, 'item', repo.persist(item));`
@ -85,7 +85,7 @@ export default Component.extend(SlotsMixin, WithListeners, {
this.listen(item, 'error', this.error.bind(this));
},
remove: function(item, items) {
const prop = get(this, 'repo').getSlugKey();
const prop = this.repo.getSlugKey();
const value = get(item, prop);
const pos = items.findIndex(function(item) {
return get(item, prop) === value;
@ -96,7 +96,7 @@ export default Component.extend(SlotsMixin, WithListeners, {
this.onchange({ target: this });
},
change: function(e, value, item) {
const event = get(this, 'dom').normalizeEvent(...arguments);
const event = this.dom.normalizeEvent(...arguments);
const items = value;
switch (event.target.name) {
case 'items[]':

View File

@ -1,5 +1,5 @@
import Component from '@ember/component';
import { get, set } from '@ember/object';
import { set } from '@ember/object';
import { inject as service } from '@ember/service';
const DEFAULTS = {
tabSize: 2,
@ -21,24 +21,24 @@ export default Component.extend({
oninput: function() {},
init: function() {
this._super(...arguments);
set(this, 'modes', get(this, 'helper').modes());
set(this, 'modes', this.helper.modes());
},
didReceiveAttrs: function() {
this._super(...arguments);
const editor = get(this, 'editor');
const editor = this.editor;
if (editor) {
editor.setOption('readOnly', get(this, 'readonly'));
editor.setOption('readOnly', this.readonly);
}
},
setMode: function(mode) {
set(this, 'options', {
...DEFAULTS,
mode: mode.mime,
readOnly: get(this, 'readonly'),
readOnly: this.readonly,
});
const editor = get(this, 'editor');
const editor = this.editor;
editor.setOption('mode', mode.mime);
get(this, 'helper').lint(editor, mode.mode);
this.helper.lint(editor, mode.mode);
set(this, 'mode', mode);
},
willDestroyElement: function() {
@ -49,7 +49,7 @@ export default Component.extend({
},
didInsertElement: function() {
this._super(...arguments);
const $code = get(this, 'dom').element('textarea ~ pre code', get(this, 'element'));
const $code = this.dom.element('textarea ~ pre code', this.element);
if ($code.firstChild) {
this.observer = new MutationObserver(([e]) => {
this.oninput(set(this, 'value', e.target.wholeText));
@ -62,27 +62,25 @@ export default Component.extend({
});
set(this, 'value', $code.firstChild.wholeText);
}
set(this, 'editor', get(this, 'helper').getEditor(this.element));
get(this, 'settings')
.findBySlug('code-editor')
.then(mode => {
const modes = get(this, 'modes');
const syntax = get(this, 'syntax');
if (syntax) {
mode = modes.find(function(item) {
return item.name.toLowerCase() == syntax.toLowerCase();
});
}
mode = !mode ? modes[0] : mode;
this.setMode(mode);
});
set(this, 'editor', this.helper.getEditor(this.element));
this.settings.findBySlug('code-editor').then(mode => {
const modes = this.modes;
const syntax = this.syntax;
if (syntax) {
mode = modes.find(function(item) {
return item.name.toLowerCase() == syntax.toLowerCase();
});
}
mode = !mode ? modes[0] : mode;
this.setMode(mode);
});
},
didAppear: function() {
get(this, 'editor').refresh();
this.editor.refresh();
},
actions: {
change: function(value) {
get(this, 'settings').persist({
this.settings.persist({
'code-editor': value,
});
this.setMode(value);

View File

@ -2,14 +2,14 @@
import Component from '@ember/component';
import SlotsMixin from 'block-slots';
import { get, set } from '@ember/object';
import { set } from '@ember/object';
import { inject as service } from '@ember/service';
const cancel = function() {
set(this, 'confirming', false);
};
const execute = function() {
this.sendAction(...['actionName', ...get(this, 'arguments')]);
this.sendAction(...['actionName', ...this['arguments']]);
};
const confirm = function() {
const [action, ...args] = arguments;
@ -18,21 +18,21 @@ const confirm = function() {
if (this._isRegistered('dialog')) {
set(this, 'confirming', true);
} else {
get(this, 'confirm')
.execute(get(this, 'message'))
this._confirm
.execute(this.message)
.then(confirmed => {
if (confirmed) {
this.execute();
}
})
.catch(function() {
return get(this, 'error').execute(...arguments);
return this.error.execute(...arguments);
});
}
};
export default Component.extend(SlotsMixin, {
classNameBindings: ['confirming'],
confirm: service('confirm'),
_confirm: service('confirm'),
error: service('error'),
classNames: ['with-confirmation'],
message: 'Are you sure?',

View File

@ -1,5 +1,4 @@
import Component from '@ember/component';
import { get } from '@ember/object';
import { inject as service } from '@ember/service';
import WithListeners from 'consul-ui/mixins/with-listeners';
@ -25,7 +24,7 @@ export default Component.extend(WithListeners, {
didInsertElement: function() {
this._super(...arguments);
const clipboard = get(this, 'clipboard').execute(
const clipboard = this.clipboard.execute(
this.delegateClickEvent ? `#${this.elementId}` : this.element
);
['success', 'error'].map(event => {

View File

@ -1,5 +1,4 @@
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
import Component from '@ember/component';
const append = function(content) {
this.element.appendChild(content);
@ -12,10 +11,10 @@ export default Component.extend({
},
didInsertElement: function() {
this._super(...arguments);
get(this, 'buffer').on('add', this.append);
this.buffer.on('add', this.append);
},
didDestroyElement: function() {
this._super(...arguments);
get(this, 'buffer').off('add', this.append);
this.buffer.off('add', this.append);
},
});

View File

@ -1,5 +1,4 @@
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
import Component from '@ember/component';
export default Component.extend({
buffer: service('dom-buffer'),
@ -10,10 +9,10 @@ export default Component.extend({
},
didInsertElement: function() {
this._super(...arguments);
get(this, 'buffer').add(this.getBufferName(), this.element);
this.buffer.add(this.getBufferName(), this.element);
},
didDestroyElement: function() {
this._super(...arguments);
get(this, 'buffer').remove(this.getBufferName());
this.buffer.remove(this.getBufferName());
},
});

View File

@ -1,5 +1,5 @@
import Component from '@ember/component';
import { get, set } from '@ember/object';
import { set } from '@ember/object';
import { inject as service } from '@ember/service';
import { Promise } from 'rsvp';
@ -21,8 +21,8 @@ export default Component.extend(SlotsMixin, {
this.error = this._error.bind(this);
},
applyTransition: function() {
const wait = get(this, 'wait').execute;
const className = get(this, 'transitionClassName');
const wait = this.wait.execute;
const className = this.transitionClassName;
// TODO: Make 0 default in wait
wait(0)
.then(() => {
@ -31,7 +31,7 @@ export default Component.extend(SlotsMixin, {
})
.then(() => {
return new Promise(resolve => {
get(this, 'dom')
this.dom
.element(`.${className}`, this.element)
.addEventListener('transitionend', resolve);
});

View File

@ -1,7 +1,6 @@
import Component from '@ember/component';
import SlotsMixin from 'block-slots';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
import { alias } from '@ember/object/computed';
import WithListeners from 'consul-ui/mixins/with-listeners';
// match anything that isn't a [ or ] into multiple groups
@ -22,7 +21,7 @@ export default Component.extend(WithListeners, SlotsMixin, {
actions: {
change: function(e, value, item) {
let event = get(this, 'dom').normalizeEvent(e, value);
let event = this.dom.normalizeEvent(e, value);
// currently form-components don't deal with deeply nested forms, only top level
// we therefore grab the end of the nest off here,
// so role[policy][Rules] will end up as policy[Rules]
@ -34,11 +33,11 @@ export default Component.extend(WithListeners, SlotsMixin, {
const prop = matches[matches.length - 1][0];
let name;
if (prop.indexOf('[') === -1) {
name = `${get(this, 'type')}[${prop}]`;
name = `${this.type}[${prop}]`;
} else {
name = prop;
}
const form = get(this, 'form');
const form = this.form;
try {
form.handleEvent(event, name);
this.onchange({ target: this });

View File

@ -1,10 +1,9 @@
import Component from '@ember/component';
import { get } from '@ember/object';
export default Component.extend({
tagName: 'fieldset',
classNames: ['freetext-filter'],
onchange: function(e) {
let searchable = get(this, 'searchable');
let searchable = this.searchable;
if (!Array.isArray(searchable)) {
searchable = [searchable];
}

View File

@ -5,18 +5,16 @@ export default Component.extend({
dom: service('dom'),
isDropdownVisible: false,
didInsertElement: function() {
get(this, 'dom')
.root()
.classList.remove('template-with-vertical-menu');
this.dom.root().classList.remove('template-with-vertical-menu');
},
actions: {
dropdown: function(e) {
if (get(this, 'dcs.length') > 0) {
set(this, 'isDropdownVisible', !get(this, 'isDropdownVisible'));
set(this, 'isDropdownVisible', !this.isDropdownVisible);
}
},
change: function(e) {
const dom = get(this, 'dom');
const dom = this.dom;
const win = dom.viewport();
const $root = dom.root();
const $body = dom.element('body');

View File

@ -1,9 +1,9 @@
import Component from '@ember/component';
import { get, computed } from '@ember/object';
import { computed } from '@ember/object';
export default Component.extend({
tagName: '',
count: computed('value', function() {
const value = get(this, 'value');
const value = this.value;
if (Array.isArray(value)) {
return value.length;
}

View File

@ -1,3 +1,4 @@
import { filter } from '@ember/object/computed';
import Component from '@ember/component';
import { computed, get } from '@ember/object';
import style from 'ember-computed-style';
@ -5,11 +6,11 @@ export default Component.extend({
classNames: ['healthchecked-resource'],
attributeBindings: ['style'],
style: style('gridRowEnd'),
unhealthy: computed.filter(`checks.@each.Status`, function(item) {
unhealthy: filter(`checks.@each.Status`, function(item) {
const status = get(item, 'Status');
return status === 'critical' || status === 'warning';
}),
healthy: computed.filter(`checks.@each.Status`, function(item) {
healthy: filter(`checks.@each.Status`, function(item) {
const status = get(item, 'Status');
return status === 'passing';
}),

View File

@ -16,9 +16,9 @@ export default DomBufferComponent.extend(SlotsMixin, WithResizing, {
onopen: function() {},
_open: function(e) {
set(this, 'checked', true);
if (get(this, 'height') === null) {
if (this.height === null) {
if (this.element) {
const dialogPanel = get(this, 'dom').element('[role="dialog"] > div > div', this.element);
const dialogPanel = this.dom.element('[role="dialog"] > div > div', this.element);
const rect = dialogPanel.getBoundingClientRect();
set(this, 'dialog', dialogPanel);
set(this, 'height', rect.height);
@ -29,25 +29,21 @@ export default DomBufferComponent.extend(SlotsMixin, WithResizing, {
},
didAppear: function() {
this._super(...arguments);
if (get(this, 'checked')) {
get(this, 'dom')
.root()
.classList.add(...templatize(['with-modal']));
if (this.checked) {
this.dom.root().classList.add(...templatize(['with-modal']));
}
},
_close: function(e) {
set(this, 'checked', false);
const dialogPanel = get(this, 'dialog');
const dialogPanel = this.dialog;
if (dialogPanel) {
const overflowing = get(this, 'overflowingClass');
const overflowing = this.overflowingClass;
if (dialogPanel.classList.contains(overflowing)) {
dialogPanel.classList.remove(overflowing);
}
}
// TODO: should we make a didDisappear?
get(this, 'dom')
.root()
.classList.remove(...templatize(['with-modal']));
this.dom.root().classList.remove(...templatize(['with-modal']));
this.onclose(e);
},
didReceiveAttrs: function() {
@ -59,11 +55,11 @@ export default DomBufferComponent.extend(SlotsMixin, WithResizing, {
// It's not our usecase just yet, but this should check the state
// of the thing its linked to, incase that has a `checked` of true
// right now we know ours is always false.
if (get(this, 'name')) {
if (this.name) {
set(this, 'checked', false);
}
if (this.element) {
if (get(this, 'checked')) {
if (this.checked) {
// TODO: probably need an event here
// possibly this.element for the target
// or find the input
@ -73,7 +69,7 @@ export default DomBufferComponent.extend(SlotsMixin, WithResizing, {
},
didInsertElement: function() {
this._super(...arguments);
if (get(this, 'checked')) {
if (this.checked) {
// TODO: probably need an event here
// possibly this.element for the target
// or find the input
@ -82,16 +78,14 @@ export default DomBufferComponent.extend(SlotsMixin, WithResizing, {
},
didDestroyElement: function() {
this._super(...arguments);
get(this, 'dom')
.root()
.classList.remove(...templatize(['with-modal']));
this.dom.root().classList.remove(...templatize(['with-modal']));
},
resize: function(e) {
if (get(this, 'checked')) {
const height = get(this, 'height');
if (this.checked) {
const height = this.height;
if (height !== null) {
const dialogPanel = get(this, 'dialog');
const overflowing = get(this, 'overflowingClass');
const dialogPanel = this.dialog;
const overflowing = this.overflowingClass;
if (height > e.detail.height) {
if (!dialogPanel.classList.contains(overflowing)) {
dialogPanel.classList.add(overflowing);
@ -114,9 +108,9 @@ export default DomBufferComponent.extend(SlotsMixin, WithResizing, {
}
},
close: function() {
const $close = get(this, 'dom').element('#modal_close');
const $close = this.dom.element('#modal_close');
$close.checked = true;
const $input = get(this, 'dom').element('input[name="modal"]', this.element);
const $input = this.dom.element('input[name="modal"]', this.element);
$input.onchange({ target: $input });
},
},

View File

@ -1,12 +1,11 @@
import DomBufferFlushComponent from 'consul-ui/components/dom-buffer-flush';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
export default DomBufferFlushComponent.extend({
dom: service('dom'),
actions: {
change: function(e) {
[...get(this, 'dom').elements('[name="modal"]')]
[...this.dom.elements('[name="modal"]')]
.filter(function(item) {
return item.getAttribute('id') !== 'modal_close';
})

View File

@ -1,5 +1,5 @@
import Component from '@ember/component';
import { get, set } from '@ember/object';
import { set } from '@ember/object';
import { inject as service } from '@ember/service';
export default Component.extend({
@ -9,18 +9,18 @@ export default Component.extend({
didInsertElement: function() {
this._super(...arguments);
// TODO: use {{ref}}
this.input = get(this, 'dom').element('input', this.element);
this.input = this.dom.element('input', this.element);
},
onchange: function(e) {},
search: function(e) {
// TODO: Temporarily continue supporting `searchable`
let searchable = get(this, 'searchable');
let searchable = this.searchable;
if (searchable) {
if (!Array.isArray(searchable)) {
searchable = [searchable];
}
searchable.forEach(item => {
item.search(get(this, 'value'));
item.search(this.value);
});
}
this.onchange(e);
@ -31,8 +31,8 @@ export default Component.extend({
keydown: function(e) {
switch (e.keyCode) {
case 8: // backspace
if (e.target.value == '' && get(this, 'value').length > 0) {
this.actions.remove.bind(this)(get(this, 'value').length - 1);
if (e.target.value == '' && this.value.length > 0) {
this.actions.remove.bind(this)(this.value.length - 1);
}
break;
case 27: // escape
@ -47,15 +47,15 @@ export default Component.extend({
this.oninput({ target: this });
},
remove: function(index, e) {
get(this, 'value').removeAt(index, 1);
this.value.removeAt(index, 1);
this.search({ target: this });
this.input.focus();
},
add: function(e) {
const item = get(this, 'item').trim();
const item = this.item.trim();
if (item !== '') {
set(this, 'item', '');
const currentItems = get(this, 'value') || [];
const currentItems = this.value || [];
const items = new Set(currentItems).add(item);
if (items.size > currentItems.length) {
set(this, 'value', [...items]);

View File

@ -13,7 +13,7 @@ export default FormComponent.extend({
init: function() {
this._super(...arguments);
set(this, 'isScoped', get(this, 'item.Datacenters.length') > 0);
set(this, 'datacenters', get(this, 'datacenterRepo').findAll());
set(this, 'datacenters', this.datacenterRepo.findAll());
this.templates = [
{
name: 'Policy',
@ -30,7 +30,7 @@ export default FormComponent.extend({
try {
this._super(...arguments);
} catch (err) {
const scoped = get(this, 'isScoped');
const scoped = this.isScoped;
const name = err.target.name;
switch (name) {
case 'policy[isScoped]':
@ -38,7 +38,7 @@ export default FormComponent.extend({
set(this, 'previousDatacenters', get(this.item, 'Datacenters'));
set(this.item, 'Datacenters', null);
} else {
set(this.item, 'Datacenters', get(this, 'previousDatacenters'));
set(this.item, 'Datacenters', this.previousDatacenters);
set(this, 'previousDatacenters', null);
}
set(this, 'isScoped', !scoped);
@ -46,7 +46,7 @@ export default FormComponent.extend({
default:
this.onerror(err);
}
this.onchange({ target: get(this, 'form') });
this.onchange({ target: this.form });
}
},
},

View File

@ -15,7 +15,7 @@ export default ChildSelectorComponent.extend({
classNames: ['policy-selector'],
init: function() {
this._super(...arguments);
const source = get(this, 'source');
const source = this.source;
if (source) {
const event = 'save';
this.listen(source, event, e => {
@ -26,16 +26,14 @@ export default ChildSelectorComponent.extend({
reset: function(e) {
this._super(...arguments);
set(this, 'isScoped', false);
set(this, 'datacenters', get(this, 'datacenterRepo').findAll());
set(this, 'datacenters', this.datacenterRepo.findAll());
},
refreshCodeEditor: function(e, target) {
const selector = '.code-editor';
get(this, 'dom')
.component(selector, target)
.didAppear();
this.dom.component(selector, target).didAppear();
},
error: function(e) {
const item = get(this, 'item');
const item = this.item;
const err = e.error;
if (typeof err.errors !== 'undefined') {
const error = err.errors[0];
@ -75,9 +73,9 @@ export default ChildSelectorComponent.extend({
}
// potentially the item could change between load, so we don't check
// anything to see if its already loaded here
const repo = get(this, 'repo');
const repo = this.repo;
// TODO: Temporarily add dc here, will soon be serialized onto the policy itself
const dc = get(this, 'dc');
const dc = this.dc;
const slugKey = repo.getSlugKey();
const slug = get(value, slugKey);
updateArrayObject(items, repo.findBySlug(slug, dc), slugKey, slug);

View File

@ -1,6 +1,6 @@
import ChildSelectorComponent from './child-selector';
import { inject as service } from '@ember/service';
import { get, set } from '@ember/object';
import { set } from '@ember/object';
import { alias } from '@ember/object/computed';
import { CallableEventSource as EventSource } from 'consul-ui/utils/dom/event-source';
@ -17,27 +17,25 @@ export default ChildSelectorComponent.extend({
policy: alias('policyForm.data'),
init: function() {
this._super(...arguments);
this.policyForm = get(this, 'formContainer').form('policy');
this.policyForm = this.formContainer.form('policy');
this.source = new EventSource();
},
actions: {
reset: function(e) {
this._super(...arguments);
get(this, 'policyForm').clear({ Datacenter: get(this, 'dc') });
this.policyForm.clear({ Datacenter: this.dc });
},
dispatch: function(type, data) {
this.source.dispatchEvent({ type: type, data: data });
},
change: function() {
const event = get(this, 'dom').normalizeEvent(...arguments);
const event = this.dom.normalizeEvent(...arguments);
const target = event.target;
switch (target.name) {
case 'role[state]':
set(this, 'state', target.value);
if (target.value === 'policy') {
get(this, 'dom')
.component('.code-editor', target.nextElementSibling)
.didAppear();
this.dom.component('.code-editor', target.nextElementSibling).didAppear();
}
break;
default:

View File

@ -1,5 +1,5 @@
import Component from '@ember/component';
import { get, set } from '@ember/object';
import { set } from '@ember/object';
export default Component.extend({
classNames: ['sort-control'],
direction: 'asc',
@ -9,7 +9,7 @@ export default Component.extend({
if (e.target.type === 'checkbox') {
set(this, 'direction', e.target.checked ? 'desc' : 'asc');
}
this.onchange({ target: { value: `${get(this, 'value')}:${get(this, 'direction')}` } });
this.onchange({ target: { value: `${this.value}:${this.direction}` } });
},
},
});

View File

@ -4,7 +4,7 @@ import { computed } from '@ember/object';
export default Component.extend({
classNames: ['tab-section'],
'data-test-radiobutton': computed('name,id', function() {
return `${this.get('name')}_${this.get('id')}`;
return `${this.name}_${this.id}`;
}),
name: 'tab',
onchange: function() {},

View File

@ -1,7 +1,7 @@
import Component from '@ember/component';
import SlotsMixin from 'block-slots';
import { inject as service } from '@ember/service';
import { get, set } from '@ember/object';
import { set } from '@ember/object';
import { subscribe } from 'consul-ui/utils/computed/purify';
let uid = 0;
@ -17,7 +17,7 @@ export default Component.extend(SlotsMixin, {
}),
actions: {
click: function(e) {
get(this, 'dom').clickFirstAnchor(e);
this.dom.clickFirstAnchor(e);
},
change: function(item, items, e) {
this.onchange(e, item, items);

View File

@ -1,3 +1,6 @@
/*eslint ember/require-return-from-computed: "warn"*/
// TODO: Remove ^
import Component from '@ember/component';
import { get, set, computed } from '@ember/object';

View File

@ -30,6 +30,8 @@ export default Component.extend({
distances: computed('tomography', function() {
const tomography = get(this, 'tomography');
let distances = get(tomography, 'distances') || [];
// TODO: This should probably be moved into the milliseconds computedProperty
/*eslint ember/no-side-effects: "warn"*/
distances.forEach((d, i) => {
if (d.distance > get(this, 'max')) {
set(this, 'max', d.distance);

View File

@ -1,11 +1,11 @@
import ComputedProperty from '@ember/object/computed';
import computedFactory from 'consul-ui/utils/computed/factory';
import { computed as computedPropertyFactory } from '@ember/object';
export default class Catchable extends ComputedProperty {
catch(cb) {
export const computed = function() {
const prop = computedPropertyFactory(...arguments);
prop.catch = function(cb) {
return this.meta({
catch: cb,
});
}
}
export const computed = computedFactory(Catchable);
};
return prop;
};

View File

@ -1,13 +1,12 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
export default Controller.extend({
builder: service('form'),
dom: service('dom'),
init: function() {
this._super(...arguments);
this.form = get(this, 'builder').form('acl');
this.form = this.builder.form('acl');
},
setProperties: function(model) {
// essentially this replaces the data with changesets
@ -24,8 +23,8 @@ export default Controller.extend({
},
actions: {
change: function(e, value, item) {
const event = get(this, 'dom').normalizeEvent(e, value);
get(this, 'form').handleEvent(event);
const event = this.dom.normalizeEvent(e, value);
this.form.handleEvent(event);
},
},
});

View File

@ -1,11 +1,10 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
export default Controller.extend({
builder: service('form'),
init: function() {
this._super(...arguments);
this.form = get(this, 'builder').form('policy');
this.form = this.builder.form('policy');
},
setProperties: function(model) {
// essentially this replaces the data with changesets

View File

@ -16,7 +16,7 @@ export default Controller.extend(WithSearching, {
},
searchable: computed('items', function() {
return get(this, 'searchables.policy')
.add(get(this, 'items'))
.add(this.items)
.search(get(this, this.searchParams.policy));
}),
actions: {},

View File

@ -1,11 +1,10 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
export default Controller.extend({
builder: service('form'),
init: function() {
this._super(...arguments);
this.form = get(this, 'builder').form('role');
this.form = this.builder.form('role');
},
setProperties: function(model) {
// essentially this replaces the data with changesets

View File

@ -16,7 +16,7 @@ export default Controller.extend(WithSearching, {
},
searchable: computed('items', function() {
return get(this, 'searchables.role')
.add(get(this, 'items'))
.add(this.items)
.search(get(this, this.searchParams.role));
}),
actions: {},

View File

@ -1,13 +1,12 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
export default Controller.extend({
dom: service('dom'),
builder: service('form'),
isScoped: false,
init: function() {
this._super(...arguments);
this.form = get(this, 'builder').form('token');
this.form = this.builder.form('token');
},
setProperties: function(model) {
// essentially this replaces the data with changesets
@ -24,8 +23,8 @@ export default Controller.extend({
},
actions: {
change: function(e, value, item) {
const event = get(this, 'dom').normalizeEvent(e, value);
const form = get(this, 'form');
const event = this.dom.normalizeEvent(e, value);
const form = this.form;
try {
form.handleEvent(event);
} catch (err) {

View File

@ -6,7 +6,7 @@ export default Controller.extend({
builder: service('form'),
init: function() {
this._super(...arguments);
this.form = get(this, 'builder').form('intention');
this.form = this.builder.form('intention');
},
setProperties: function(model) {
const sourceName = get(model.item, 'SourceName');
@ -35,11 +35,11 @@ export default Controller.extend({
return template.replace(/{{term}}/g, term);
},
isUnique: function(term) {
return !get(this, 'items').findBy('Name', term);
return !this.items.findBy('Name', term);
},
change: function(e, value, item) {
const event = get(this, 'dom').normalizeEvent(e, value);
const form = get(this, 'form');
const event = this.dom.normalizeEvent(e, value);
const form = this.form;
const target = event.target;
let name;
@ -55,7 +55,7 @@ export default Controller.extend({
name = get(target.value, 'Name');
}
// see if the name is already in the list
match = get(this, 'items').filterBy('Name', name);
match = this.items.filterBy('Name', name);
if (match.length === 0) {
// if its not make a new 'fake' Service that doesn't exist yet
// and add it to the possible services to make an intention between

View File

@ -9,7 +9,7 @@ export default Controller.extend({
json: true,
init: function() {
this._super(...arguments);
this.form = get(this, 'builder').form('kv');
this.form = this.builder.form('kv');
},
setProperties: function(model) {
// essentially this replaces the data with changesets
@ -26,8 +26,8 @@ export default Controller.extend({
},
actions: {
change: function(e, value, item) {
const event = get(this, 'dom').normalizeEvent(e, value);
const form = get(this, 'form');
const event = this.dom.normalizeEvent(e, value);
const form = this.form;
try {
form.handleEvent(event);
} catch (err) {
@ -35,7 +35,7 @@ export default Controller.extend({
let parent;
switch (target.name) {
case 'value':
set(this.item, 'Value', get(this, 'encoder').execute(target.value));
set(this.item, 'Value', this.encoder.execute(target.value));
break;
case 'additional':
parent = get(this, 'parent.Key');
@ -47,7 +47,7 @@ export default Controller.extend({
// even if the user has selected code=off on another KV
// ideally we would save the value per KV, but I'd like to not do that on the model
// a set(this, 'json', valueFromSomeStorageJustForThisKV) would be added here
set(this, 'json', !get(this, 'json'));
set(this, 'json', !this.json);
break;
default:
throw err;

View File

@ -16,7 +16,7 @@ export default Controller.extend(WithSearching, {
},
searchable: computed('items', function() {
return get(this, 'searchables.kv')
.add(get(this, 'items'))
.add(this.items)
.search(get(this, this.searchParams.kv));
}),
});

View File

@ -14,21 +14,21 @@ export default Controller.extend(WithEventSource, WithSearching, WithHealthFilte
},
searchableHealthy: computed('healthy', function() {
return get(this, 'searchables.healthyNode')
.add(get(this, 'healthy'))
.add(this.healthy)
.search(get(this, this.searchParams.healthyNode));
}),
searchableUnhealthy: computed('unhealthy', function() {
return get(this, 'searchables.unhealthyNode')
.add(get(this, 'unhealthy'))
.add(this.unhealthy)
.search(get(this, this.searchParams.unhealthyNode));
}),
unhealthy: computed('filtered', function() {
return get(this, 'filtered').filter(function(item) {
return this.filtered.filter(function(item) {
return get(item, 'isUnhealthy');
});
}),
healthy: computed('filtered', function() {
return get(this, 'filtered').filter(function(item) {
return this.filtered.filter(function(item) {
return get(item, 'isHealthy');
});
}),

View File

@ -25,20 +25,20 @@ export default Controller.extend(WithEventSource, WithSearching, {
if (e.target.readyState === 1) {
// OPEN
if (get(e, 'error.errors.firstObject.status') === '404') {
get(this, 'notify').add({
this.notify.add({
destroyOnClick: false,
sticky: true,
type: 'warning',
action: 'update',
});
get(this, 'tomography').close();
get(this, 'sessions').close();
this.tomography.close();
this.sessions.close();
}
}
}),
searchable: computed('items', function() {
return get(this, 'searchables.nodeservice')
.add(get(this, 'items'))
.add(this.items)
.search(get(this, this.searchParams.nodeservice));
}),
setProperties: function() {
@ -55,7 +55,7 @@ export default Controller.extend(WithEventSource, WithSearching, {
set(this, 'selectedTab', e.target.value);
// Ensure tabular-collections sizing is recalculated
// now it is visible in the DOM
get(this, 'dom')
this.dom
.components('.tab-section input[type="radio"]:checked + div table')
.forEach(function(item) {
if (typeof item.didAppear === 'function') {

View File

@ -39,8 +39,8 @@ export default Controller.extend(WithEventSource, WithSearching, {
},
searchable: computed('items.[]', function() {
return get(this, 'searchables.service')
.add(get(this, 'items'))
.search(get(this, 'terms'));
.add(this.items)
.search(this.terms);
}),
maxWidth: computed('{maxPassing,maxWarning,maxCritical}', function() {
const PADDING = 32 * 3 + 13;
@ -49,7 +49,7 @@ export default Controller.extend(WithEventSource, WithSearching, {
}, PADDING);
}),
totalWidth: computed('maxWidth', function() {
return widthDeclaration(get(this, 'maxWidth'));
return widthDeclaration(this.maxWidth);
}),
remainingWidth: computed('maxWidth', function() {
// maxWidth is the maximum width of the healthchecks column
@ -57,24 +57,24 @@ export default Controller.extend(WithEventSource, WithSearching, {
// take that off 50% (100% / number of fluid columns)
// also we added a Type column which we've currently fixed to 100px
// so again divide that by 2 and take it off each fluid column
return htmlSafe(`width: calc(50% - ${Math.round(get(this, 'maxWidth') / 2)}px)`);
return htmlSafe(`width: calc(50% - ${Math.round(this.maxWidth / 2)}px)`);
}),
maxPassing: computed('items.[]', function() {
return max(get(this, 'items'), 'ChecksPassing');
return max(this.items, 'ChecksPassing');
}),
maxWarning: computed('items.[]', function() {
return max(get(this, 'items'), 'ChecksWarning');
return max(this.items, 'ChecksWarning');
}),
maxCritical: computed('items.[]', function() {
return max(get(this, 'items'), 'ChecksCritical');
return max(this.items, 'ChecksCritical');
}),
passingWidth: computed('maxPassing', function() {
return widthDeclaration(width(get(this, 'maxPassing')));
return widthDeclaration(width(this.maxPassing));
}),
warningWidth: computed('maxWarning', function() {
return widthDeclaration(width(get(this, 'maxWarning')));
return widthDeclaration(width(this.maxWarning));
}),
criticalWidth: computed('maxCritical', function() {
return widthDeclaration(width(get(this, 'maxCritical')));
return widthDeclaration(width(this.maxCritical));
}),
});

View File

@ -16,13 +16,13 @@ export default Controller.extend(WithEventSource, {
if (e.target.readyState === 1) {
// OPEN
if (get(e, 'error.errors.firstObject.status') === '404') {
get(this, 'notify').add({
this.notify.add({
destroyOnClick: false,
sticky: true,
type: 'warning',
action: 'update',
});
const proxy = get(this, 'proxy');
const proxy = this.proxy;
if (proxy) {
proxy.close();
}

View File

@ -25,7 +25,7 @@ export default Controller.extend(WithEventSource, WithSearching, {
if (e.target.readyState === 1) {
// OPEN
if (get(e, 'error.errors.firstObject.status') === '404') {
get(this, 'notify').add({
this.notify.add({
destroyOnClick: false,
sticky: true,
type: 'warning',
@ -36,7 +36,7 @@ export default Controller.extend(WithEventSource, WithSearching, {
}),
searchable: computed('items', function() {
return get(this, 'searchables.serviceInstance')
.add(get(this, 'items'))
.add(this.items)
.search(get(this, this.searchParams.serviceInstance));
}),
actions: {
@ -44,7 +44,7 @@ export default Controller.extend(WithEventSource, WithSearching, {
set(this, 'selectedTab', e.target.value);
// Ensure tabular-collections sizing is recalculated
// now it is visible in the DOM
get(this, 'dom')
this.dom
.components('.tab-section input[type="radio"]:checked + div table')
.forEach(function(item) {
if (typeof item.didAppear === 'function') {

View File

@ -8,7 +8,7 @@ export default Controller.extend({
timeout: service('timeout'),
confirming: false,
applyTransition: function() {
const tick = get(this, 'timeout').tick;
const tick = this.timeout.tick;
set(this, 'confirming', true);
tick().then(() => {
set(this, 'confirming', false);
@ -24,7 +24,7 @@ export default Controller.extend({
}
},
change: function(e, value, item) {
const event = get(this, 'dom').normalizeEvent(e, value);
const event = this.dom.normalizeEvent(e, value);
// TODO: Switch to using forms like the rest of the app
// setting utils/form/builder for things to be done before we
// can do that. For the moment just do things normally its a simple
@ -35,14 +35,14 @@ export default Controller.extend({
switch (target.name) {
case 'client[blocking]':
set(this, 'item.client.blocking', !blocking);
this.send('update', get(this, 'item'));
this.send('update', this.item);
break;
case 'urls[service]':
if (typeof get(this, 'item.urls') === 'undefined') {
set(this, 'item.urls', {});
}
set(this, 'item.urls.service', target.value);
this.send('update', get(this, 'item'));
this.send('update', this.item);
break;
}
},

View File

@ -12,20 +12,18 @@ const isWildcard = wildcard(routes);
export default Helper.extend({
compute([targetRouteName, ...rest], namedArgs) {
if (isWildcard(targetRouteName)) {
rest = rest.map(function(item, i) {
return item
.split('/')
.map(encodeURIComponent)
.join('/');
});
}
if (namedArgs.params) {
return hrefTo(this, ...namedArgs.params);
return hrefTo(this, namedArgs.params);
} else {
if (isWildcard(targetRouteName)) {
const split = rest.map(function(item, i) {
return item
.split('/')
.map(encodeURIComponent)
.join('/');
});
return hrefTo(this, targetRouteName, ...split);
} else {
return hrefTo(this, targetRouteName, ...rest);
}
return hrefTo(this, [targetRouteName, ...rest]);
}
},
});

View File

@ -1,3 +1,5 @@
/*eslint ember/no-observers: "warn"*/
// TODO: Remove ^
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { observer } from '@ember/object';
@ -5,7 +7,7 @@ import { observer } from '@ember/object';
export default Helper.extend({
router: service('router'),
compute(params) {
return this.get('router').isActive(...params);
return this.router.isActive(...params);
},
onURLChange: observer('router.currentURL', function() {
this.recompute();

View File

@ -1,9 +1,8 @@
import { get } from '@ember/object';
export function initialize(application) {
const PowerSelectComponent = application.resolveRegistration('component:power-select');
PowerSelectComponent.reopen({
updateState: function(changes) {
if (!get(this, 'isDestroyed')) {
if (!this.isDestroyed) {
return this._super(changes);
}
},

View File

@ -7,11 +7,11 @@ export default Mixin.create(WithBlockingActions, {
settings: service('settings'),
actions: {
use: function(item) {
return get(this, 'feedback').execute(() => {
return this.feedback.execute(() => {
// old style legacy ACLs don't have AccessorIDs
// therefore set it to null, this way the frontend knows
// to use legacy ACLs
return get(this, 'settings')
return this.settings
.persist({
token: {
AccessorID: null,
@ -25,28 +25,24 @@ export default Mixin.create(WithBlockingActions, {
},
// TODO: This is also used in tokens, probably an opportunity to dry this out
logout: function(item) {
return get(this, 'feedback').execute(() => {
return get(this, 'settings')
.delete('token')
.then(() => {
// in this case we don't do the same as delete as we want to go to the new
// dc.acls.tokens page. If we get there via the dc.acls redirect/rewrite
// then we lose the flash message
return this.transitionTo('dc.acls.tokens');
});
return this.feedback.execute(() => {
return this.settings.delete('token').then(() => {
// in this case we don't do the same as delete as we want to go to the new
// dc.acls.tokens page. If we get there via the dc.acls redirect/rewrite
// then we lose the flash message
return this.transitionTo('dc.acls.tokens');
});
}, 'logout');
},
clone: function(item) {
return get(this, 'feedback').execute(() => {
return get(this, 'repo')
.clone(item)
.then(item => {
// cloning is similar to delete in that
// if you clone from the listing page, stay on the listing page
// whereas if you clone form another token, take me back to the listing page
// so I can see it
return this.afterDelete(...arguments);
});
return this.feedback.execute(() => {
return this.repo.clone(item).then(item => {
// cloning is similar to delete in that
// if you clone from the listing page, stay on the listing page
// whereas if you clone form another token, take me back to the listing page
// so I can see it
return this.afterDelete(...arguments);
});
}, 'clone');
},
},

View File

@ -1,7 +1,6 @@
import Mixin from '@ember/object/mixin';
import { inject as service } from '@ember/service';
import { next } from '@ember/runloop';
import { get } from '@ember/object';
// TODO: Potentially move this to dom service
const isOutside = function(element, e, doc = document) {
@ -15,7 +14,7 @@ const isOutside = function(element, e, doc = document) {
};
const handler = function(e) {
const el = get(this, 'element');
const el = this.element;
if (isOutside(el, e)) {
this.onblur(e);
}
@ -30,14 +29,14 @@ export default Mixin.create({
onblur: function() {},
didInsertElement: function() {
this._super(...arguments);
const doc = get(this, 'dom').document();
const doc = this.dom.document();
next(this, () => {
doc.addEventListener('click', this.handler);
});
},
willDestroyElement: function() {
this._super(...arguments);
const doc = get(this, 'dom').document();
const doc = this.dom.document();
doc.removeEventListener('click', this.handler);
},
});

View File

@ -10,7 +10,7 @@ import { get } from '@ember/object';
*/
export default Mixin.create({
beforeModel: function() {
get(this, 'repo').invalidate();
this.repo.invalidate();
},
deactivate: function() {
// TODO: This is dependent on ember-changeset

View File

@ -21,8 +21,8 @@ export default Mixin.create(WithBlockingActions, {
actions: {
invalidateSession: function(item) {
const controller = this.controller;
const repo = get(this, 'sessionRepo');
return get(this, 'feedback').execute(() => {
const repo = this.sessionRepo;
return this.feedback.execute(() => {
return repo.remove(item).then(() => {
const item = get(controller, 'item');
set(item, 'Session', null);

View File

@ -1,5 +1,3 @@
import { REQUEST_CREATE, REQUEST_UPDATE } from 'consul-ui/adapters/application';
import Mixin from '@ember/object/mixin';
import { get } from '@ember/object';
@ -17,6 +15,8 @@ const normalizeServiceIdentities = function(items) {
return policy;
});
};
// Sometimes we get `Policies: null`, make null equal an empty array
// and add an empty template
const normalizePolicies = function(items) {
return (items || []).map(function(item) {
return {
@ -47,24 +47,36 @@ const serializePolicies = function(items) {
};
export default Mixin.create({
handleSingleResponse: function(url, response, primary, slug) {
response.Policies = normalizePolicies(response.Policies).concat(
normalizeServiceIdentities(response.ServiceIdentities)
);
return this._super(url, response, primary, slug);
//TODO: what about update and create?
respondForQueryRecord: function(respond, query) {
return this._super(function(cb) {
return respond((headers, body) => {
body.Policies = normalizePolicies(body.Policies).concat(
normalizeServiceIdentities(body.ServiceIdentities)
);
return cb(headers, body);
});
}, query);
},
dataForRequest: function(params) {
respondForQuery: function(respond, query) {
return this._super(function(cb) {
return respond(function(headers, body) {
return cb(
headers,
body.map(function(item) {
item.Policies = normalizePolicies(item.Policies).concat(
normalizeServiceIdentities(item.ServiceIdentities)
);
return item;
})
);
});
}, query);
},
serialize: function(snapshot, options) {
const data = this._super(...arguments);
const name = params.type.modelName;
switch (params.requestType) {
case REQUEST_UPDATE:
// falls through
case REQUEST_CREATE:
// ServiceIdentities serialization must happen first, or a copy taken
data[name].ServiceIdentities = serializeServiceIdentities(data[name].Policies);
data[name].Policies = minimizeModel(serializePolicies(data[name].Policies));
break;
}
data.ServiceIdentities = serializeServiceIdentities(data.Policies);
data.Policies = minimizeModel(serializePolicies(data.Policies));
return data;
},
});

View File

@ -1,28 +1,33 @@
import { REQUEST_CREATE, REQUEST_UPDATE } from 'consul-ui/adapters/application';
import Mixin from '@ember/object/mixin';
import minimizeModel from 'consul-ui/utils/minimizeModel';
export default Mixin.create({
handleSingleResponse: function(url, response, primary, slug) {
['Roles'].forEach(function(prop) {
if (typeof response[prop] === 'undefined' || response[prop] === null) {
response[prop] = [];
}
});
return this._super(url, response, primary, slug);
// TODO: what about update and create?
respondForQueryRecord: function(respond, query) {
return this._super(function(cb) {
return respond((headers, body) => {
body.Roles = typeof body.Roles === 'undefined' || body.Roles === null ? [] : body.Roles;
return cb(headers, body);
});
}, query);
},
dataForRequest: function(params) {
const name = params.type.modelName;
respondForQuery: function(respond, query) {
return this._super(function(cb) {
return respond(function(headers, body) {
return cb(
headers,
body.map(function(item) {
item.Roles = typeof item.Roles === 'undefined' || item.Roles === null ? [] : item.Roles;
return item;
})
);
});
}, query);
},
serialize: function(snapshot, options) {
const data = this._super(...arguments);
switch (params.requestType) {
case REQUEST_UPDATE:
// falls through
case REQUEST_CREATE:
data[name].Roles = minimizeModel(data[name].Roles);
break;
}
data.Roles = minimizeModel(data.Roles);
return data;
},
});

View File

@ -7,11 +7,11 @@ export default Mixin.create(WithBlockingActions, {
settings: service('settings'),
actions: {
use: function(item) {
return get(this, 'feedback').execute(() => {
return get(this, 'repo')
return this.feedback.execute(() => {
return this.repo
.findBySlug(get(item, 'AccessorID'), this.modelFor('dc').dc.Name)
.then(item => {
return get(this, 'settings')
return this.settings
.persist({
token: {
AccessorID: get(item, 'AccessorID'),
@ -28,21 +28,19 @@ export default Mixin.create(WithBlockingActions, {
}, 'use');
},
logout: function(item) {
return get(this, 'feedback').execute(() => {
return get(this, 'settings')
.delete('token')
.then(() => {
// logging out is similar to delete in that
// if you log out from the listing page, stay on the listing page
// whereas if you logout from the detail page, take me back to the listing page
return this.afterDelete(...arguments);
});
return this.feedback.execute(() => {
return this.settings.delete('token').then(() => {
// logging out is similar to delete in that
// if you log out from the listing page, stay on the listing page
// whereas if you logout from the detail page, take me back to the listing page
return this.afterDelete(...arguments);
});
}, 'logout');
},
clone: function(item) {
let cloned;
return get(this, 'feedback').execute(() => {
return get(this, 'repo')
return this.feedback.execute(() => {
return this.repo
.clone(item)
.then(item => {
cloned = item;

View File

@ -1,6 +1,6 @@
import Mixin from '@ember/object/mixin';
import { inject as service } from '@ember/service';
import { get, set } from '@ember/object';
import { set } from '@ember/object';
/** With Blocking Actions
* This mixin contains common write actions (Create Update Delete) for routes.
* It could also be an Route to extend but decoration seems to be more sense right now.
@ -20,7 +20,7 @@ export default Mixin.create({
_feedback: service('feedback'),
init: function() {
this._super(...arguments);
const feedback = get(this, '_feedback');
const feedback = this._feedback;
const route = this;
set(this, 'feedback', {
execute: function(cb, type, error) {
@ -69,13 +69,11 @@ export default Mixin.create({
return this.afterUpdate(...arguments);
},
create: function(item) {
return get(this, 'feedback').execute(
return this.feedback.execute(
() => {
return get(this, 'repo')
.persist(item)
.then(item => {
return this.afterCreate(...arguments);
});
return this.repo.persist(item).then(item => {
return this.afterCreate(...arguments);
});
},
'create',
(type, e) => {
@ -84,13 +82,11 @@ export default Mixin.create({
);
},
update: function(item) {
return get(this, 'feedback').execute(
return this.feedback.execute(
() => {
return get(this, 'repo')
.persist(item)
.then(() => {
return this.afterUpdate(...arguments);
});
return this.repo.persist(item).then(() => {
return this.afterUpdate(...arguments);
});
},
'update',
(type, e) => {
@ -99,13 +95,11 @@ export default Mixin.create({
);
},
delete: function(item) {
return get(this, 'feedback').execute(
return this.feedback.execute(
() => {
return get(this, 'repo')
.remove(item)
.then(() => {
return this.afterDelete(...arguments);
});
return this.repo.remove(item).then(() => {
return this.afterDelete(...arguments);
});
},
'delete',
(type, e) => {

View File

@ -1,4 +1,5 @@
import Mixin from '@ember/object/mixin';
import { set } from '@ember/object';
import { computed as catchable } from 'consul-ui/computed/catchable';
import purify from 'consul-ui/utils/computed/purify';
@ -9,13 +10,23 @@ export default Mixin.create(WithListeners, {
const _model = {};
Object.keys(model).forEach(prop => {
// here (see comment below on deleting)
if (this[prop] && this[prop].isDescriptor) {
_model[`${PREFIX}${prop}`] = model[prop];
const meta = this.constructor.metaForProperty(prop) || {};
if (model[prop] && typeof model[prop].addEventListener === 'function') {
let meta;
// TODO: metaForProperty throws an error if the property is not
// computed-like, this is far from ideal but happy with this
// until we can find a better way in an ember post 2.18 world
// of finding out if a property is computed or not
// (or until we switch all this out for <DataSource /> compoments
try {
meta = this.constructor.metaForProperty(prop);
} catch (e) {
meta = {};
}
if (typeof meta.catch === 'function') {
if (typeof _model[`${PREFIX}${prop}`].addEventListener === 'function') {
this.listen(_model[`_${prop}`], 'error', meta.catch.bind(this));
}
_model[`${PREFIX}${prop}`] = model[prop];
this.listen(_model[`_${prop}`], 'error', meta.catch.bind(this));
} else {
_model[prop] = model[prop];
}
} else {
_model[prop] = model[prop];
@ -33,7 +44,10 @@ export default Mixin.create(WithListeners, {
// setProperties will be called the next time we enter the Route so this
// is ok for what we need and means that the above conditional works
// as expected (see 'here' comment above)
delete this[prop];
// delete this[prop];
// TODO: Check that nulling this out instead of deleting is fine
// pretty sure it is as above is just a falsey check
set(this, prop, null);
}
});
}

View File

@ -39,7 +39,7 @@ export default Mixin.create({
set(this, key, obj[key] != '' ? obj[key] : null);
});
set(this, 'filters', {
...this.get('filters'),
...this.filters,
...obj,
});
},

View File

@ -2,13 +2,12 @@ import Controller from '@ember/controller';
import Component from '@ember/component';
import Mixin from '@ember/object/mixin';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
export default Mixin.create({
dom: service('dom'),
init: function() {
this._super(...arguments);
this._listeners = get(this, 'dom').listeners();
this._listeners = this.dom.listeners();
let teardown = ['willDestroy'];
if (this instanceof Component) {
teardown = ['willDestroyElement'];

View File

@ -1,6 +1,5 @@
import Mixin from '@ember/object/mixin';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
import { assert } from '@ember/debug';
export default Mixin.create({
dom: service('dom'),
@ -18,18 +17,14 @@ export default Mixin.create({
},
didInsertElement: function() {
this._super(...arguments);
get(this, 'dom')
.viewport()
.addEventListener('resize', this.handler, false);
this.dom.viewport().addEventListener('resize', this.handler, false);
this.didAppear();
},
didAppear: function() {
this.handler({ target: get(this, 'dom').viewport() });
this.handler({ target: this.dom.viewport() });
},
willDestroyElement: function() {
get(this, 'dom')
.viewport()
.removeEventListener('resize', this.handler, false);
this.dom.viewport().removeEventListener('resize', this.handler, false);
this._super(...arguments);
},
});

View File

@ -1,6 +1,6 @@
import Mixin from '@ember/object/mixin';
import { inject as service } from '@ember/service';
import { get, set } from '@ember/object';
import { set } from '@ember/object';
import WithListeners from 'consul-ui/mixins/with-listeners';
/**
* WithSearching mostly depends on a `searchParams` object which must be set
@ -22,7 +22,7 @@ export default Mixin.create(WithListeners, {
this.searchables = {};
Object.keys(params).forEach(type => {
const key = params[type];
this.searchables[type] = get(this, 'builder').searchable(type);
this.searchables[type] = this.builder.searchable(type);
this.listen(this.searchables[type], 'change', e => {
const value = e.target.value;
set(this, key, value === '' ? null : value);

View File

@ -1,7 +1,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
import { next } from '@ember/runloop';
import { Promise } from 'rsvp';
@ -19,11 +18,11 @@ export default Route.extend(WithBlockingActions, {
settings: service('settings'),
actions: {
loading: function(transition, originRoute) {
const $root = get(this, 'dom').root();
const $root = this.dom.root();
let dc = null;
if (originRoute.routeName !== 'dc') {
const model = this.modelFor('dc') || { dcs: null, dc: { Name: null } };
dc = get(this, 'repo').getActive(model.dc.Name, model.dcs);
dc = this.repo.getActive(model.dc.Name, model.dcs);
}
hash({
loading: !$root.classList.contains('ember-loading'),
@ -66,23 +65,21 @@ export default Route.extend(WithBlockingActions, {
// if a token has not been sent at all, it just gives you a 200 with an empty dataset
const model = this.modelFor('dc');
if (error.status === '403') {
return get(this, 'feedback').execute(() => {
return get(this, 'settings')
.delete('token')
.then(() => {
return Promise.reject(this.transitionTo('dc.acls.tokens', model.dc.Name));
});
return this.feedback.execute(() => {
return this.settings.delete('token').then(() => {
return Promise.reject(this.transitionTo('dc.acls.tokens', model.dc.Name));
});
}, 'authorize');
}
if (error.status === '') {
error.message = 'Error';
}
const $root = get(this, 'dom').root();
const $root = this.dom.root();
hash({
error: error,
dc:
error.status.toString().indexOf('5') !== 0
? get(this, 'repo').getActive()
? this.repo.getActive()
: model && model.dc
? model.dc
: { Name: 'Error' },

View File

@ -1,12 +1,11 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
export default Route.extend({
repo: service('repository/dc'),
settings: service('settings'),
model: function(params) {
const repo = get(this, 'repo');
const repo = this.repo;
return hash({
dcs: repo.findAll(),
}).then(function(model) {

View File

@ -9,32 +9,30 @@ export default Route.extend(WithBlockingActions, {
actions: {
authorize: function(secret) {
const dc = this.modelFor('dc').dc.Name;
return get(this, 'feedback').execute(() => {
return get(this, 'repo')
.self(secret, dc)
.then(item => {
return get(this, 'settings')
.persist({
token: {
AccessorID: get(item, 'AccessorID'),
SecretID: secret,
},
})
.then(item => {
// a null AccessorID means we are in legacy mode
// take the user to the legacy acls
// otherwise just refresh the page
if (get(item, 'token.AccessorID') === null) {
// returning false for a feedback action means even though
// its successful, please skip this notification and don't display it
return this.transitionTo('dc.acls').then(function() {
return false;
});
} else {
this.refresh();
}
});
});
return this.feedback.execute(() => {
return this.repo.self(secret, dc).then(item => {
return this.settings
.persist({
token: {
AccessorID: get(item, 'AccessorID'),
SecretID: secret,
},
})
.then(item => {
// a null AccessorID means we are in legacy mode
// take the user to the legacy acls
// otherwise just refresh the page
if (get(item, 'token.AccessorID') === null) {
// returning false for a feedback action means even though
// its successful, please skip this notification and don't display it
return this.transitionTo('dc.acls').then(function() {
return false;
});
} else {
this.refresh();
}
});
});
}, 'authorize');
},
},

View File

@ -9,10 +9,10 @@ export default Route.extend(WithAclActions, {
templateName: 'dc/acls/edit',
repo: service('repository/acl'),
beforeModel: function() {
get(this, 'repo').invalidate();
this.repo.invalidate();
},
model: function(params) {
this.item = get(this, 'repo').create();
this.item = this.repo.create();
set(this.item, 'Datacenter', this.modelFor('dc').dc.Name);
return hash({
create: true,

View File

@ -1,7 +1,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
import WithAclActions from 'consul-ui/mixins/acl/with-actions';
@ -11,7 +10,7 @@ export default Route.extend(WithAclActions, {
model: function(params) {
return hash({
isLoading: false,
item: get(this, 'repo').findBySlug(params.id, this.modelFor('dc').dc.Name),
item: this.repo.findBySlug(params.id, this.modelFor('dc').dc.Name),
types: ['management', 'client'],
});
},

View File

@ -15,24 +15,22 @@ export default Route.extend(WithAclActions, {
},
},
beforeModel: function(transition) {
return get(this, 'settings')
.findBySlug('token')
.then(token => {
// If you don't have a token set or you have a
// token set with AccessorID set to not null (new ACL mode)
// then rewrite to the new acls
if (!token || get(token, 'AccessorID') !== null) {
// If you return here, you get a TransitionAborted error in the tests only
// everything works fine either way checking things manually
this.replaceWith('dc.acls.tokens');
}
});
return this.settings.findBySlug('token').then(token => {
// If you don't have a token set or you have a
// token set with AccessorID set to not null (new ACL mode)
// then rewrite to the new acls
if (!token || get(token, 'AccessorID') !== null) {
// If you return here, you get a TransitionAborted error in the tests only
// everything works fine either way checking things manually
this.replaceWith('dc.acls.tokens');
}
});
},
model: function(params) {
return hash({
isLoading: false,
items: get(this, 'repo').findAllByDatacenter(this.modelFor('dc').dc.Name),
token: get(this, 'settings').findBySlug('token'),
items: this.repo.findAllByDatacenter(this.modelFor('dc').dc.Name),
token: this.settings.findBySlug('token'),
});
},
setupController: function(controller, model) {

View File

@ -10,7 +10,7 @@ export default SingleRoute.extend(WithPolicyActions, {
tokenRepo: service('repository/token'),
model: function(params) {
const dc = this.modelFor('dc').dc.Name;
const tokenRepo = get(this, 'tokenRepo');
const tokenRepo = this.tokenRepo;
return this._super(...arguments).then(model => {
return hash({
...model,

View File

@ -1,7 +1,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
import WithPolicyActions from 'consul-ui/mixins/policy/with-actions';
@ -14,7 +13,7 @@ export default Route.extend(WithPolicyActions, {
},
},
model: function(params) {
const repo = get(this, 'repo');
const repo = this.repo;
return hash({
...repo.status({
items: repo.findAllByDatacenter(this.modelFor('dc').dc.Name),

View File

@ -10,7 +10,7 @@ export default SingleRoute.extend(WithRoleActions, {
tokenRepo: service('repository/token'),
model: function(params) {
const dc = this.modelFor('dc').dc.Name;
const tokenRepo = get(this, 'tokenRepo');
const tokenRepo = this.tokenRepo;
return this._super(...arguments).then(model => {
return hash({
...model,

View File

@ -1,7 +1,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
import WithRoleActions from 'consul-ui/mixins/role/with-actions';
@ -14,7 +13,7 @@ export default Route.extend(WithRoleActions, {
},
},
model: function(params) {
const repo = get(this, 'repo');
const repo = this.repo;
return hash({
...repo.status({
items: repo.findAllByDatacenter(this.modelFor('dc').dc.Name),

View File

@ -1,7 +1,6 @@
import SingleRoute from 'consul-ui/routing/single';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
import WithTokenActions from 'consul-ui/mixins/token/with-actions';
@ -13,7 +12,7 @@ export default SingleRoute.extend(WithTokenActions, {
return hash({
...model,
...{
token: get(this, 'settings').findBySlug('token'),
token: this.settings.findBySlug('token'),
},
});
});

View File

@ -13,26 +13,24 @@ export default Route.extend(WithTokenActions, {
},
},
beforeModel: function(transition) {
return get(this, 'settings')
.findBySlug('token')
.then(token => {
// If you have a token set with AccessorID set to null (legacy mode)
// then rewrite to the old acls
if (token && get(token, 'AccessorID') === null) {
// If you return here, you get a TransitionAborted error in the tests only
// everything works fine either way checking things manually
this.replaceWith('dc.acls');
}
});
return this.settings.findBySlug('token').then(token => {
// If you have a token set with AccessorID set to null (legacy mode)
// then rewrite to the old acls
if (token && get(token, 'AccessorID') === null) {
// If you return here, you get a TransitionAborted error in the tests only
// everything works fine either way checking things manually
this.replaceWith('dc.acls');
}
});
},
model: function(params) {
const repo = get(this, 'repo');
const repo = this.repo;
return hash({
...repo.status({
items: repo.findAllByDatacenter(this.modelFor('dc').dc.Name),
}),
isLoading: false,
token: get(this, 'settings').findBySlug('token'),
token: this.settings.findBySlug('token'),
});
},
setupController: function(controller, model) {

View File

@ -9,16 +9,16 @@ export default Route.extend(WithIntentionActions, {
repo: service('repository/intention'),
servicesRepo: service('repository/service'),
beforeModel: function() {
get(this, 'repo').invalidate();
this.repo.invalidate();
},
model: function(params) {
this.item = get(this, 'repo').create();
this.item = this.repo.create();
set(this.item, 'Datacenter', this.modelFor('dc').dc.Name);
return hash({
create: true,
isLoading: false,
item: this.item,
items: get(this, 'servicesRepo').findAllByDatacenter(this.modelFor('dc').dc.Name),
items: this.servicesRepo.findAllByDatacenter(this.modelFor('dc').dc.Name),
intents: ['allow', 'deny'],
}).then(function(model) {
return {

View File

@ -11,8 +11,8 @@ export default Route.extend(WithAclActions, {
model: function(params) {
return hash({
isLoading: false,
item: get(this, 'repo').findBySlug(params.id, this.modelFor('dc').dc.Name),
items: get(this, 'servicesRepo').findAllByDatacenter(this.modelFor('dc').dc.Name),
item: this.repo.findBySlug(params.id, this.modelFor('dc').dc.Name),
items: this.servicesRepo.findAllByDatacenter(this.modelFor('dc').dc.Name),
intents: ['allow', 'deny'],
}).then(function(model) {
return {

View File

@ -1,7 +1,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
import WithIntentionActions from 'consul-ui/mixins/intention/with-actions';
@ -15,7 +14,7 @@ export default Route.extend(WithIntentionActions, {
},
model: function(params) {
return hash({
items: get(this, 'repo').findAllByDatacenter(this.modelFor('dc').dc.Name),
items: this.repo.findAllByDatacenter(this.modelFor('dc').dc.Name),
});
},
setupController: function(controller, model) {

View File

@ -8,11 +8,11 @@ export default Route.extend(WithKvActions, {
templateName: 'dc/kv/edit',
repo: service('repository/kv'),
beforeModel: function() {
get(this, 'repo').invalidate();
this.repo.invalidate();
},
model: function(params) {
const key = params.key || '/';
const repo = get(this, 'repo');
const repo = this.repo;
const dc = this.modelFor('dc').dc.Name;
this.item = repo.create();
set(this.item, 'Datacenter', dc);

View File

@ -12,7 +12,7 @@ export default Route.extend(WithKvActions, {
model: function(params) {
const key = params.key;
const dc = this.modelFor('dc').dc.Name;
const repo = get(this, 'repo');
const repo = this.repo;
return hash({
isLoading: false,
parent: repo.findBySlug(ascend(key, 1) || '/', dc),
@ -25,7 +25,7 @@ export default Route.extend(WithKvActions, {
return hash({
...model,
...{
session: get(this, 'sessionRepo').findByKey(session, dc),
session: this.sessionRepo.findByKey(session, dc),
},
});
}

View File

@ -25,7 +25,7 @@ export default Route.extend(WithKvActions, {
model: function(params) {
let key = params.key || '/';
const dc = this.modelFor('dc').dc.Name;
const repo = get(this, 'repo');
const repo = this.repo;
return hash({
isLoading: false,
parent: repo.findBySlug(key, dc),

View File

@ -1,7 +1,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
export default Route.extend({
repo: service('repository/node'),
@ -14,8 +13,8 @@ export default Route.extend({
model: function(params) {
const dc = this.modelFor('dc').dc.Name;
return hash({
items: get(this, 'repo').findAllByDatacenter(dc),
leader: get(this, 'repo').findByLeader(dc),
items: this.repo.findAllByDatacenter(dc),
leader: this.repo.findByLeader(dc),
});
},
setupController: function(controller, model) {

View File

@ -19,9 +19,9 @@ export default Route.extend(WithBlockingActions, {
const dc = this.modelFor('dc').dc.Name;
const name = params.name;
return hash({
item: get(this, 'repo').findBySlug(name, dc),
tomography: get(this, 'coordinateRepo').findAllByNode(name, dc),
sessions: get(this, 'sessionRepo').findByNode(name, dc),
item: this.repo.findBySlug(name, dc),
tomography: this.coordinateRepo.findAllByNode(name, dc),
sessions: this.sessionRepo.findByNode(name, dc),
});
},
setupController: function(controller, model) {
@ -31,8 +31,8 @@ export default Route.extend(WithBlockingActions, {
invalidateSession: function(item) {
const dc = this.modelFor('dc').dc.Name;
const controller = this.controller;
const repo = get(this, 'sessionRepo');
return get(this, 'feedback').execute(() => {
const repo = this.sessionRepo;
return this.feedback.execute(() => {
const node = get(item, 'Node');
return repo.remove(item).then(() => {
return repo.findByNode(node, dc).then(function(sessions) {

View File

@ -1,7 +1,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
export default Route.extend({
repo: service('repository/service'),
@ -16,7 +15,7 @@ export default Route.extend({
},
},
model: function(params) {
const repo = get(this, 'repo');
const repo = this.repo;
let terms = params.s || '';
// we check for the old style `status` variable here
// and convert it to the new style filter=status:critical

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