From fbbdc4d352f74bd7a3ea46943f1eed03c87d590b Mon Sep 17 00:00:00 2001 From: John Cowen Date: Tue, 23 Feb 2021 08:56:42 +0000 Subject: [PATCH] ui: DataSource Decorator (#9746) We use a `` component throughout our UI for when we want to load data from within our components. The URL specified as the `@src` is used to map/lookup what is used in to retrieve data, for example we mostly use our repository methods wrapped with our Promise backed `EventSource` implementation, but DataSource URLs can also be mapped to EventTarget backed `EventSource`s and native `EventSource`s or `WebSockets` if we ever need to use those (for example these are options for potential streaming support with the Consul backend). The URL to function/method mapping previous to this PR used a very naive humongous `switch` statement which was a temporary 'this is fine for the moment' solution, although we'd always wanted to replace with something more manageable. Here we add `wayfarer` as a dependency - a very small (1kb), very fast, radix trie based router, and use that to perform the URL to function/method mapping. This essentially turns every `DataSource` into a very small SPA - change its URL and the view of data changes. When the data itself changes, either the yielded view of data changes or the `onchange` event is fired with the changed data, making the externally sourced view of data completely reactive. ```javascript // use the new decorator a service somewhere to annotate/decorate // a method with the URL that can be used to access this method @dataSource('/:ns/:dc/services') async findAllByDatacenter(params) { // get the data } // can use with JS in a route somewhere async model() { return this.data.source(uri => uri`/${nspace}/${dc}/services`) } ``` ```hbs {{!-- or just straight in a template using the component --}} ``` This also uses a new `container` Service to automatically execute/import certain services yet not execute them. This new service also provides a lookup that supports both standard ember DI lookup plus Class based lookup or these specific services. Lastly we also provide another debug function called DataSourceRoutes() which can be called from console which gives you a list of URLs and their mappings. --- .../consul-ui/app/adapters/permission.js | 8 +- .../consul-ui/app/decorators/data-source.js | 52 ++++ .../app/instance-initializers/container.js | 32 +++ .../app/mixins/token/with-actions.js | 10 +- ui/packages/consul-ui/app/routes/dc.js | 10 +- .../app/routes/dc/acls/auth-methods/index.js | 8 +- .../consul-ui/app/routes/dc/acls/edit.js | 5 +- .../consul-ui/app/routes/dc/acls/index.js | 2 +- .../app/routes/dc/acls/policies/edit.js | 24 +- .../app/routes/dc/acls/policies/index.js | 8 +- .../app/routes/dc/acls/roles/edit.js | 24 +- .../app/routes/dc/acls/roles/index.js | 8 +- .../app/routes/dc/acls/tokens/index.js | 8 +- .../app/routes/dc/intentions/edit.js | 6 +- .../consul-ui/app/routes/dc/kv/edit.js | 24 +- .../consul-ui/app/routes/dc/kv/index.js | 22 +- .../consul-ui/app/routes/dc/nspaces/edit.js | 2 +- ui/packages/consul-ui/app/routing/single.js | 6 +- .../consul-ui/app/services/container.js | 36 +++ .../services/data-source/protocols/http.js | 238 ++---------------- .../consul-ui/app/services/repository.js | 71 +++--- .../app/services/repository/auth-method.js | 11 + .../app/services/repository/coordinate.js | 23 +- .../consul-ui/app/services/repository/dc.js | 4 +- .../services/repository/discovery-chain.js | 6 +- .../app/services/repository/intention.js | 28 ++- .../consul-ui/app/services/repository/kv.js | 47 ++-- .../app/services/repository/metrics.js | 59 +++-- .../consul-ui/app/services/repository/node.js | 21 +- .../app/services/repository/nspace.js | 9 +- .../app/services/repository/nspace/enabled.js | 2 +- .../app/services/repository/oidc-provider.js | 23 +- .../app/services/repository/permission.js | 30 ++- .../app/services/repository/policy.js | 11 + .../app/services/repository/proxy.js | 27 +- .../consul-ui/app/services/repository/role.js | 11 + .../services/repository/service-instance.js | 42 ++-- .../app/services/repository/service.js | 23 +- .../app/services/repository/session.js | 18 +- .../app/services/repository/token.js | 27 +- .../app/services/repository/topology.js | 20 +- ui/packages/consul-ui/package.json | 3 +- .../services/repository/acl-test.js | 4 +- .../services/repository/auth-method-test.js | 5 +- .../services/repository/coordinate-test.js | 8 +- .../repository/discovery-chain-test.js | 2 +- .../services/repository/kv-test.js | 4 +- .../services/repository/node-test.js | 4 +- .../services/repository/policy-test.js | 4 +- .../services/repository/role-test.js | 4 +- .../services/repository/service-test.js | 2 +- .../services/repository/session-test.js | 4 +- .../services/repository/token-test.js | 8 +- .../services/repository/topology-test.js | 2 +- ui/yarn.lock | 12 + 55 files changed, 582 insertions(+), 530 deletions(-) create mode 100644 ui/packages/consul-ui/app/decorators/data-source.js create mode 100644 ui/packages/consul-ui/app/instance-initializers/container.js create mode 100644 ui/packages/consul-ui/app/services/container.js diff --git a/ui/packages/consul-ui/app/adapters/permission.js b/ui/packages/consul-ui/app/adapters/permission.js index cdcee918d..dcd357ae2 100644 --- a/ui/packages/consul-ui/app/adapters/permission.js +++ b/ui/packages/consul-ui/app/adapters/permission.js @@ -4,20 +4,20 @@ import { inject as service } from '@ember/service'; export default class PermissionAdapter extends Adapter { @service('env') env; - requestForAuthorize(request, { dc, ns, permissions = [], index }) { + requestForAuthorize(request, { dc, ns, resources = [], index }) { // the authorize endpoint is slightly different to all others in that it // ignores an ns parameter, but accepts a Namespace property on each // resource. Here we hide this different from the rest of the app as - // currently we never need to ask for permissions/resources for mutiple + // currently we never need to ask for permissions/resources for multiple // different namespaces in one call so here we use the ns param and add // this to the resources instead of passing through on the queryParameter if (this.env.var('CONSUL_NSPACES_ENABLED')) { - permissions = permissions.map(item => ({ ...item, Namespace: ns })); + resources = resources.map(item => ({ ...item, Namespace: ns })); } return request` POST /v1/internal/acl/authorize?${{ dc, index }} - ${permissions} + ${resources} `; } diff --git a/ui/packages/consul-ui/app/decorators/data-source.js b/ui/packages/consul-ui/app/decorators/data-source.js new file mode 100644 index 000000000..39626f86d --- /dev/null +++ b/ui/packages/consul-ui/app/decorators/data-source.js @@ -0,0 +1,52 @@ +import { runInDebug } from '@ember/debug'; +import wayfarer from 'wayfarer'; + +const router = wayfarer(); +const routes = {}; +export default path => (target, propertyKey, desc) => { + runInDebug(() => { + routes[path] = { cls: target, method: propertyKey }; + }); + router.on(path, function(params, owner) { + const container = owner.lookup('service:container'); + const instance = container.get(target); + return configuration => desc.value.apply(instance, [params, configuration]); + }); + return desc; +}; +export const match = path => { + return router.match(path); +}; + +runInDebug(() => { + window.DataSourceRoutes = () => { + // debug time only way to access the application and be able to lookup + // services, don't use ConsulUi global elsewhere! + const container = window.ConsulUi.__container__.lookup('service:container'); + const win = window.open('', '_blank'); + win.document.write(` + +
+${Object.entries(routes)
+  .map(([key, value]) => {
+    let cls = container
+      .keyForClass(value.cls)
+      .split('/')
+      .pop();
+    cls = cls
+      .split('-')
+      .map(item => `${item[0].toUpperCase()}${item.substr(1)}`)
+      .join('');
+    return `${key}
+      ${cls}Repository.${value.method}(params)
+
+`;
+  })
+  .join('')}
+  
+ + `); + win.focus(); + return; + }; +}); diff --git a/ui/packages/consul-ui/app/instance-initializers/container.js b/ui/packages/consul-ui/app/instance-initializers/container.js new file mode 100644 index 000000000..ee19684ca --- /dev/null +++ b/ui/packages/consul-ui/app/instance-initializers/container.js @@ -0,0 +1,32 @@ +import { runInDebug } from '@ember/debug'; + +export default { + name: 'container', + initialize(application) { + const container = application.lookup('service:container'); + // find all the services and add their classes to the container so we can + // look instances up by class afterwards as we then resolve the + // registration for each of these further down this means that any top + // level code for these services is executed, this is most useful for + // making sure any annotation type decorators are executed. + // For now we only want repositories, so only look for those for the moment + let repositories = container + .get('container-debug-adapter:main') + .catalogEntriesByType('service') + .filter(item => item.startsWith('repository/')); + + // during testing we get -test files in here, filter those out but only in debug envs + runInDebug(() => (repositories = repositories.filter(item => !item.endsWith('-test')))); + + // 'service' service is not returned by catalogEntriesByType, possibly + // related to pods and the service being called 'service': + // https://github.com/ember-cli/ember-resolver/blob/c07287af17766bfd3acf390f867fea17686f77d2/addon/resolvers/classic/container-debug-adapter.js#L80 + // so push it on the end + repositories.push('repository/service'); + // + repositories.forEach(item => { + const key = `service:${item}`; + container.set(key, container.resolveRegistration(key)); + }); + }, +}; diff --git a/ui/packages/consul-ui/app/mixins/token/with-actions.js b/ui/packages/consul-ui/app/mixins/token/with-actions.js index 6c9509e42..f3d1ad2a3 100644 --- a/ui/packages/consul-ui/app/mixins/token/with-actions.js +++ b/ui/packages/consul-ui/app/mixins/token/with-actions.js @@ -8,11 +8,11 @@ export default Mixin.create(WithBlockingActions, { actions: { use: function(item) { return this.repo - .findBySlug( - get(item, 'AccessorID'), - this.modelFor('dc').dc.Name, - this.modelFor('nspace').nspace.substr(1) - ) + .findBySlug({ + ns: this.modelFor('nspace').nspace.substr(1), + dc: this.modelFor('dc').dc.Name, + id: get(item, 'AccessorID'), + }) .then(item => { return this.settings.persist({ token: { diff --git a/ui/packages/consul-ui/app/routes/dc.js b/ui/packages/consul-ui/app/routes/dc.js index 19404acff..5fafb2926 100644 --- a/ui/packages/consul-ui/app/routes/dc.js +++ b/ui/packages/consul-ui/app/routes/dc.js @@ -43,7 +43,10 @@ export default class DcRoute extends Route { app.nspaces.length > 1 ? findActiveNspace(app.nspaces, nspace) : app.nspaces.firstObject; // When disabled nspaces is [], so nspace is undefined - const permissions = await this.permissionsRepo.findAll(params.dc, get(nspace || {}, 'Name')); + const permissions = await this.permissionsRepo.findAll({ + dc: params.dc, + nspace: get(nspace || {}, 'Name'), + }); return { dc, nspace, @@ -79,7 +82,10 @@ export default class DcRoute extends Route { const controller = this.controllerFor('application'); Promise.all([ this.nspacesRepo.findAll(), - this.permissionsRepo.findAll(get(controller, 'dc.Name'), get(controller, 'nspace.Name')), + this.permissionsRepo.findAll({ + dc: get(controller, 'dc.Name'), + nspace: get(controller, 'nspace.Name'), + }), ]).then(([nspaces, permissions]) => { if (typeof controller !== 'undefined') { controller.setProperties({ diff --git a/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/index.js b/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/index.js index f45d92397..4702ec091 100644 --- a/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/index.js +++ b/ui/packages/consul-ui/app/routes/dc/acls/auth-methods/index.js @@ -22,10 +22,10 @@ export default class IndexRoute extends Route { model(params) { return hash({ ...this.repo.status({ - items: this.repo.findAllByDatacenter( - this.modelFor('dc').dc.Name, - this.modelFor('nspace').nspace.substr(1) - ), + items: this.repo.findAllByDatacenter({ + dc: this.modelFor('dc').dc.Name, + ns: this.modelFor('nspace').nspace.substr(1), + }), }), searchProperties: this.queryParams.searchproperty.empty[0], }); diff --git a/ui/packages/consul-ui/app/routes/dc/acls/edit.js b/ui/packages/consul-ui/app/routes/dc/acls/edit.js index 60ed4371e..0835c4e9e 100644 --- a/ui/packages/consul-ui/app/routes/dc/acls/edit.js +++ b/ui/packages/consul-ui/app/routes/dc/acls/edit.js @@ -13,7 +13,10 @@ export default class EditRoute extends Route.extend(WithAclActions) { model(params) { return hash({ - item: this.repo.findBySlug(params.id, this.modelFor('dc').dc.Name), + item: this.repo.findBySlug({ + dc: this.modelFor('dc').dc.Name, + id: params.id, + }), types: ['management', 'client'], }); } diff --git a/ui/packages/consul-ui/app/routes/dc/acls/index.js b/ui/packages/consul-ui/app/routes/dc/acls/index.js index c5761cc43..2e896fc29 100644 --- a/ui/packages/consul-ui/app/routes/dc/acls/index.js +++ b/ui/packages/consul-ui/app/routes/dc/acls/index.js @@ -30,7 +30,7 @@ export default class IndexRoute extends Route.extend(WithAclActions) { } async model(params) { - const _items = this.repo.findAllByDatacenter(this.modelFor('dc').dc.Name); + const _items = this.repo.findAllByDatacenter({ dc: this.modelFor('dc').dc.Name }); const _token = this.settings.findBySlug('token'); return { items: await _items, diff --git a/ui/packages/consul-ui/app/routes/dc/acls/policies/edit.js b/ui/packages/consul-ui/app/routes/dc/acls/policies/edit.js index 5705a180a..145952393 100644 --- a/ui/packages/consul-ui/app/routes/dc/acls/policies/edit.js +++ b/ui/packages/consul-ui/app/routes/dc/acls/policies/edit.js @@ -21,15 +21,21 @@ export default class EditRoute extends SingleRoute.extend(WithPolicyActions) { ...model, ...{ routeName: this.routeName, - items: tokenRepo.findByPolicy(get(model.item, 'ID'), dc, nspace).catch(function(e) { - switch (get(e, 'errors.firstObject.status')) { - case '403': - case '401': - // do nothing the SingleRoute will have caught it already - return; - } - throw e; - }), + items: tokenRepo + .findByPolicy({ + ns: nspace, + dc: dc, + id: get(model.item, 'ID'), + }) + .catch(function(e) { + switch (get(e, 'errors.firstObject.status')) { + case '403': + case '401': + // do nothing the SingleRoute will have caught it already + return; + } + throw e; + }), }, }); }); diff --git a/ui/packages/consul-ui/app/routes/dc/acls/policies/index.js b/ui/packages/consul-ui/app/routes/dc/acls/policies/index.js index 394f4c6f2..68a3fba07 100644 --- a/ui/packages/consul-ui/app/routes/dc/acls/policies/index.js +++ b/ui/packages/consul-ui/app/routes/dc/acls/policies/index.js @@ -26,10 +26,10 @@ export default class IndexRoute extends Route.extend(WithPolicyActions) { model(params) { return hash({ ...this.repo.status({ - items: this.repo.findAllByDatacenter( - this.modelFor('dc').dc.Name, - this.modelFor('nspace').nspace.substr(1) - ), + items: this.repo.findAllByDatacenter({ + ns: this.modelFor('nspace').nspace.substr(1), + dc: this.modelFor('dc').dc.Name, + }), }), searchProperties: this.queryParams.searchproperty.empty[0], }); diff --git a/ui/packages/consul-ui/app/routes/dc/acls/roles/edit.js b/ui/packages/consul-ui/app/routes/dc/acls/roles/edit.js index e45862e96..524d5ce80 100644 --- a/ui/packages/consul-ui/app/routes/dc/acls/roles/edit.js +++ b/ui/packages/consul-ui/app/routes/dc/acls/roles/edit.js @@ -20,15 +20,21 @@ export default class EditRoute extends SingleRoute.extend(WithRoleActions) { return hash({ ...model, ...{ - items: tokenRepo.findByRole(get(model.item, 'ID'), dc, nspace).catch(function(e) { - switch (get(e, 'errors.firstObject.status')) { - case '403': - case '401': - // do nothing the SingleRoute will have caught it already - return; - } - throw e; - }), + items: tokenRepo + .findByRole({ + ns: nspace, + dc: dc, + id: get(model.item, 'ID'), + }) + .catch(function(e) { + switch (get(e, 'errors.firstObject.status')) { + case '403': + case '401': + // do nothing the SingleRoute will have caught it already + return; + } + throw e; + }), }, }); }); diff --git a/ui/packages/consul-ui/app/routes/dc/acls/roles/index.js b/ui/packages/consul-ui/app/routes/dc/acls/roles/index.js index 46c14f493..ab06f02d1 100644 --- a/ui/packages/consul-ui/app/routes/dc/acls/roles/index.js +++ b/ui/packages/consul-ui/app/routes/dc/acls/roles/index.js @@ -22,10 +22,10 @@ export default class IndexRoute extends Route.extend(WithRoleActions) { model(params) { return hash({ ...this.repo.status({ - items: this.repo.findAllByDatacenter( - this.modelFor('dc').dc.Name, - this.modelFor('nspace').nspace.substr(1) - ), + items: this.repo.findAllByDatacenter({ + ns: this.modelFor('nspace').nspace.substr(1), + dc: this.modelFor('dc').dc.Name, + }), }), searchProperties: this.queryParams.searchproperty.empty[0], }); diff --git a/ui/packages/consul-ui/app/routes/dc/acls/tokens/index.js b/ui/packages/consul-ui/app/routes/dc/acls/tokens/index.js index bfc26b5f9..1441e1ea1 100644 --- a/ui/packages/consul-ui/app/routes/dc/acls/tokens/index.js +++ b/ui/packages/consul-ui/app/routes/dc/acls/tokens/index.js @@ -35,10 +35,10 @@ export default class IndexRoute extends Route.extend(WithTokenActions) { model(params) { return hash({ ...this.repo.status({ - items: this.repo.findAllByDatacenter( - this.modelFor('dc').dc.Name, - this.modelFor('nspace').nspace.substr(1) - ), + items: this.repo.findAllByDatacenter({ + ns: this.modelFor('nspace').nspace.substr(1), + dc: this.modelFor('dc').dc.Name, + }), }), nspace: this.modelFor('nspace').nspace.substr(1), token: this.settings.findBySlug('token'), diff --git a/ui/packages/consul-ui/app/routes/dc/intentions/edit.js b/ui/packages/consul-ui/app/routes/dc/intentions/edit.js index 36aa0fd6e..52d150235 100644 --- a/ui/packages/consul-ui/app/routes/dc/intentions/edit.js +++ b/ui/packages/consul-ui/app/routes/dc/intentions/edit.js @@ -11,7 +11,11 @@ export default class EditRoute extends Route { let item; if (typeof intention_id !== 'undefined') { - item = await this.repo.findBySlug(intention_id, dc, nspace); + item = await this.repo.findBySlug({ + ns: nspace, + dc: dc, + id: intention_id, + }); } else { const defaultNspace = this.env.var('CONSUL_NSPACES_ENABLED') ? '*' : 'default'; item = await this.repo.create({ diff --git a/ui/packages/consul-ui/app/routes/dc/kv/edit.js b/ui/packages/consul-ui/app/routes/dc/kv/edit.js index 127332cf9..f18fca372 100644 --- a/ui/packages/consul-ui/app/routes/dc/kv/edit.js +++ b/ui/packages/consul-ui/app/routes/dc/kv/edit.js @@ -24,14 +24,26 @@ export default class EditRoute extends Route { nspace: nspace || 'default', parent: typeof key !== 'undefined' - ? this.repo.findBySlug(ascend(key, 1) || '/', dc, nspace) - : this.repo.findBySlug('/', dc, nspace), + ? this.repo.findBySlug({ + ns: nspace, + dc: dc, + id: ascend(key, 1) || '/', + }) + : this.repo.findBySlug({ + ns: nspace, + dc: dc, + id: '/', + }), item: create ? this.repo.create({ Datacenter: dc, Namespace: nspace, }) - : this.repo.findBySlug(key, dc, nspace), + : this.repo.findBySlug({ + ns: nspace, + dc: dc, + id: key, + }), session: null, }).then(model => { // TODO: Consider loading this after initial page load @@ -41,7 +53,11 @@ export default class EditRoute extends Route { return hash({ ...model, ...{ - session: this.sessionRepo.findByKey(session, dc, nspace), + session: this.sessionRepo.findByKey({ + ns: nspace, + dc: dc, + id: session, + }), }, }); } diff --git a/ui/packages/consul-ui/app/routes/dc/kv/index.js b/ui/packages/consul-ui/app/routes/dc/kv/index.js index bbbccde3d..5b043c6a3 100644 --- a/ui/packages/consul-ui/app/routes/dc/kv/index.js +++ b/ui/packages/consul-ui/app/routes/dc/kv/index.js @@ -31,12 +31,30 @@ export default class IndexRoute extends Route { const dc = this.modelFor('dc').dc.Name; const nspace = this.modelFor('nspace').nspace.substr(1); return hash({ - parent: this.repo.findBySlug(key, dc, nspace), + parent: this.repo.findBySlug({ + ns: nspace, + dc: dc, + id: key, + }), }).then(model => { return hash({ ...model, ...{ - items: this.repo.findAllBySlug(get(model.parent, 'Key'), dc, nspace), + items: this.repo + .findAllBySlug({ + ns: nspace, + dc: dc, + id: get(model.parent, 'Key'), + }) + .catch(e => { + const status = get(e, 'errors.firstObject.status'); + switch (status) { + case '403': + return this.transitionTo('dc.acls.tokens'); + default: + return this.transitionTo('dc.kv.index'); + } + }), }, }); }); diff --git a/ui/packages/consul-ui/app/routes/dc/nspaces/edit.js b/ui/packages/consul-ui/app/routes/dc/nspaces/edit.js index 28dc3408b..8d619c7eb 100644 --- a/ui/packages/consul-ui/app/routes/dc/nspaces/edit.js +++ b/ui/packages/consul-ui/app/routes/dc/nspaces/edit.js @@ -28,7 +28,7 @@ export default class EditRoute extends Route.extend(WithNspaceActions) { }, }) ) - : repo.findBySlug(params.name), + : repo.findBySlug({ id: params.name }), }); } diff --git a/ui/packages/consul-ui/app/routing/single.js b/ui/packages/consul-ui/app/routing/single.js index d221aa8ab..4ee0ea35f 100644 --- a/ui/packages/consul-ui/app/routing/single.js +++ b/ui/packages/consul-ui/app/routing/single.js @@ -27,7 +27,11 @@ export default Route.extend({ Namespace: nspace, }) ) - : repo.findBySlug(params.id, dc, nspace), + : repo.findBySlug({ + ns: nspace, + dc: dc, + id: params.id, + }), }), }); }, diff --git a/ui/packages/consul-ui/app/services/container.js b/ui/packages/consul-ui/app/services/container.js new file mode 100644 index 000000000..bdd4d6fd2 --- /dev/null +++ b/ui/packages/consul-ui/app/services/container.js @@ -0,0 +1,36 @@ +import Service from '@ember/service'; + +export default class ContainerService extends Service { + constructor(owner) { + super(...arguments); + this._owner = owner; + this._wm = new WeakMap(); + } + + set(key, value) { + this._wm.set(value, key); + } + + // vaguely private, used publicly for debugging purposes + keyForClass(cls) { + return this._wm.get(cls); + } + + get(key) { + if (typeof key !== 'string') { + key = this.keyForClass(key); + } + return this.lookup(key); + } + + lookup(key) { + return this._owner.lookup(key); + } + + resolveRegistration(key) { + // ember resolveRegistration returns an ember flavoured class extending + // from the actual class, access the actual class from the + // prototype/parent which is what decorators pass through as target + return this._owner.resolveRegistration(key).prototype; + } +} diff --git a/ui/packages/consul-ui/app/services/data-source/protocols/http.js b/ui/packages/consul-ui/app/services/data-source/protocols/http.js index 97493b7b3..a771ad221 100644 --- a/ui/packages/consul-ui/app/services/data-source/protocols/http.js +++ b/ui/packages/consul-ui/app/services/data-source/protocols/http.js @@ -1,95 +1,28 @@ import Service, { inject as service } from '@ember/service'; import { get } from '@ember/object'; +import { getOwner } from '@ember/application'; +import { match } from 'consul-ui/decorators/data-source'; +import { singularize } from 'ember-inflector'; export default class HttpService extends Service { - @service('repository/dc') - datacenters; + @service('repository/dc') datacenters; + @service('repository/node') leader; + @service('repository/service') gateways; + @service('repository/service-instance') 'proxy-service-instance'; + @service('repository/proxy') 'proxy-instance'; + @service('repository/nspace') namespaces; + @service('repository/metrics') metrics; + @service('repository/oidc-provider') oidc; - @service('repository/node') - nodes; - - @service('repository/node') - node; - - @service('repository/node') - leader; - - @service('repository/service') - gateways; - - @service('repository/service') - services; - - @service('repository/service') - service; - - @service('repository/service-instance') - 'service-instance'; - - @service('repository/service-instance') - 'proxy-service-instance'; - - @service('repository/service-instance') - 'service-instances'; - - @service('repository/proxy') - proxies; - - @service('repository/proxy') - 'proxy-instance'; - - @service('repository/discovery-chain') - 'discovery-chain'; - - @service('repository/topology') - topology; - - @service('repository/coordinate') - coordinates; - - @service('repository/session') - sessions; - - @service('repository/nspace') - namespaces; - - @service('repository/intention') - intentions; - - @service('repository/intention') - intention; - - @service('repository/kv') - kv; - - @service('repository/token') - token; - - @service('repository/policy') - policies; - - @service('repository/policy') - policy; - - @service('repository/role') - roles; - - @service('repository/oidc-provider') - oidc; - - @service('repository/metrics') - metrics; - - @service('data-source/protocols/http/blocking') - type; + @service('data-source/protocols/http/blocking') type; source(src, configuration) { - // TODO: Consider adding/requiring 'action': nspace, dc, model, action, ...rest - const [, nspace, dc, model, ...rest] = src.split('/').map(decodeURIComponent); - // nspaces can be filled, blank or * - // so we might get urls like //dc/services - let find; - const repo = this[model]; + const [, , , model] = src.split('/'); + const owner = getOwner(this); + const route = match(src); + const find = route.cb(route.params, owner); + + const repo = this[model] || owner.lookup(`service:repository/${singularize(model)}`); configuration.createEvent = function(result = {}, configuration) { const event = { type: 'message', @@ -101,141 +34,6 @@ export default class HttpService extends Service { } return event; }; - let method, slug, more, protocol; - switch (model) { - case 'metrics': - [method, slug, ...more] = rest; - switch (method) { - case 'summary-for-service': - [protocol, ...more] = more; - find = configuration => - repo.findServiceSummary(protocol, slug, dc, nspace, configuration); - break; - case 'upstream-summary-for-service': - find = configuration => repo.findUpstreamSummary(slug, dc, nspace, configuration); - break; - case 'downstream-summary-for-service': - find = configuration => repo.findDownstreamSummary(slug, dc, nspace, configuration); - break; - } - break; - case 'datacenters': - case 'namespaces': - find = configuration => repo.findAll(configuration); - break; - case 'services': - case 'nodes': - case 'roles': - case 'policies': - find = configuration => repo.findAllByDatacenter(dc, nspace, configuration); - break; - case 'leader': - find = configuration => repo.findLeader(dc, configuration); - break; - case 'intentions': - [method, ...slug] = rest; - switch (method) { - case 'for-service': - find = configuration => repo.findByService(slug, dc, nspace, configuration); - break; - default: - find = configuration => repo.findAllByDatacenter(dc, nspace, configuration); - break; - } - break; - case 'service-instances': - [method, ...slug] = rest; - switch (method) { - case 'for-service': - find = configuration => repo.findByService(slug, dc, nspace, configuration); - break; - } - break; - case 'coordinates': - [method, ...slug] = rest; - switch (method) { - case 'for-node': - find = configuration => repo.findAllByNode(slug, dc, configuration); - break; - } - break; - case 'proxies': - [method, ...slug] = rest; - switch (method) { - case 'for-service': - find = configuration => repo.findAllBySlug(slug, dc, nspace, configuration); - break; - } - break; - case 'gateways': - [method, ...slug] = rest; - switch (method) { - case 'for-service': - find = configuration => repo.findGatewayBySlug(slug, dc, nspace, configuration); - break; - } - break; - case 'sessions': - [method, ...slug] = rest; - switch (method) { - case 'for-node': - find = configuration => repo.findByNode(slug, dc, nspace, configuration); - break; - } - break; - case 'token': - find = configuration => repo.self(rest[1], dc); - break; - case 'discovery-chain': - case 'node': - find = configuration => repo.findBySlug(rest[0], dc, nspace, configuration); - break; - case 'service-instance': - // id, node, service - find = configuration => - repo.findBySlug(rest[0], rest[1], rest[2], dc, nspace, configuration); - break; - case 'proxy-service-instance': - // id, node, service - find = configuration => - repo.findProxyBySlug(rest[0], rest[1], rest[2], dc, nspace, configuration); - break; - case 'proxy-instance': - // id, node, service - find = configuration => - repo.findInstanceBySlug(rest[0], rest[1], rest[2], dc, nspace, configuration); - break; - case 'topology': - // id, service kind - find = configuration => repo.findBySlug(rest[0], rest[1], dc, nspace, configuration); - break; - case 'policy': - case 'kv': - case 'intention': - slug = rest[0]; - if (slug) { - find = configuration => repo.findBySlug(slug, dc, nspace, configuration); - } else { - find = configuration => - Promise.resolve(repo.create({ Datacenter: dc, Namespace: nspace })); - } - break; - case 'oidc': - [method, ...slug] = rest; - switch (method) { - case 'providers': - find = configuration => repo.findAllByDatacenter(dc, nspace, configuration); - break; - case 'provider': - find = configuration => repo.findBySlug(slug[0], dc, nspace); - break; - case 'authorize': - find = configuration => - repo.authorize(slug[0], slug[1], slug[2], dc, nspace, configuration); - break; - } - break; - } return this.type.source(find, configuration); } } diff --git a/ui/packages/consul-ui/app/services/repository.js b/ui/packages/consul-ui/app/services/repository.js index 4d7f2181c..cdfb20eea 100644 --- a/ui/packages/consul-ui/app/services/repository.js +++ b/ui/packages/consul-ui/app/services/repository.js @@ -23,43 +23,33 @@ export default class RepositoryService extends Service { } /** - * Creates a set of permissions base don a slug, loads in the access - * permissions for themand checks/validates + * Creates a set of permissions based on an id/slug, loads in the access + * permissions for them and checks/validates */ - async authorizeBySlug(cb, access, slug, dc, nspace) { - return this.validatePermissions( - cb, - await this.permissions.findBySlug(slug, this.getModelName(), dc, nspace), - access, - dc, - nspace - ); + async authorizeBySlug(cb, access, params) { + params.resources = await this.permissions.findBySlug(params, this.getModelName()); + return this.validatePermissions(cb, access, params); } /** * Loads in the access permissions and checks/validates them for a set of * permissions */ - async authorizeByPermissions(cb, permissions, access, dc, nspace) { - return this.validatePermissions( - cb, - await this.permissions.authorize(permissions, dc, nspace), - access, - dc, - nspace - ); + async authorizeByPermissions(cb, access, params) { + params.resources = await this.permissions.authorize(params); + return this.validatePermissions(cb, access, params); } /** * Checks already loaded permissions for certain access before calling cb to * return the thing you wanted to check the permissions on */ - async validatePermissions(cb, permissions, access, dc, nspace) { + async validatePermissions(cb, access, params) { // inspect the permissions for this segment/slug remotely, if we have zero // permissions fire a fake 403 so we don't even request the model/resource - if (permissions.length > 0) { - const permission = permissions.find(item => item.Access === access); - if (permission && permission.Allow === false) { + if (params.resources.length > 0) { + const resource = params.resources.find(item => item.Access === access); + if (resource && resource.Allow === false) { // TODO: Here we temporarily make a hybrid HTTPError/ember-data HTTP error // we should eventually use HTTPError's everywhere const e = new HTTPError(403); @@ -71,7 +61,7 @@ export default class RepositoryService extends Service { // add the `Resource` information to the record/model so we can inspect // them in other places like templates etc if (get(item, 'Resources')) { - set(item, 'Resources', permissions); + set(item, 'Resources', params.resources); } return item; } @@ -102,34 +92,29 @@ export default class RepositoryService extends Service { return this.store.peekRecord(this.getModelName(), id); } - findAllByDatacenter(dc, nspace, configuration = {}) { - const query = { - dc: dc, - ns: nspace, - }; + findAllByDatacenter(params, configuration = {}) { if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; - query.uri = configuration.uri; + params.index = configuration.cursor; + params.uri = configuration.uri; } - return this.store.query(this.getModelName(), query); + return this.store.query(this.getModelName(), params); } - async findBySlug(slug, dc, nspace, configuration = {}) { - const query = { - dc: dc, - ns: nspace, - id: slug, - }; + async findBySlug(params, configuration = {}) { + if (params.id === '') { + return this.create({ + Datacenter: params.dc, + Namespace: params.ns, + }); + } if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; - query.uri = configuration.uri; + params.index = configuration.cursor; + params.uri = configuration.uri; } return this.authorizeBySlug( - () => this.store.queryRecord(this.getModelName(), query), + () => this.store.queryRecord(this.getModelName(), params), ACCESS_READ, - slug, - dc, - nspace + params ); } diff --git a/ui/packages/consul-ui/app/services/repository/auth-method.js b/ui/packages/consul-ui/app/services/repository/auth-method.js index 818b3ecac..392db0b2d 100644 --- a/ui/packages/consul-ui/app/services/repository/auth-method.js +++ b/ui/packages/consul-ui/app/services/repository/auth-method.js @@ -2,6 +2,7 @@ import RepositoryService from 'consul-ui/services/repository'; import statusFactory from 'consul-ui/utils/acls-status'; import isValidServerErrorFactory from 'consul-ui/utils/http/acl/is-valid-server-error'; import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/auth-method'; +import dataSource from 'consul-ui/decorators/data-source'; const isValidServerError = isValidServerErrorFactory(); const status = statusFactory(isValidServerError, Promise); @@ -20,6 +21,16 @@ export default class AuthMethodService extends RepositoryService { return SLUG_KEY; } + @dataSource('/:ns/:dc/auth-methods') + async findAllByDatacenter() { + return super.findAllByDatacenter(...arguments); + } + + @dataSource('/:ns/:dc/auth-method/:id') + async findBySlug() { + return super.findBySlug(...arguments); + } + status(obj) { return status(obj); } diff --git a/ui/packages/consul-ui/app/services/repository/coordinate.js b/ui/packages/consul-ui/app/services/repository/coordinate.js index 5978893f0..af0e6f3bf 100644 --- a/ui/packages/consul-ui/app/services/repository/coordinate.js +++ b/ui/packages/consul-ui/app/services/repository/coordinate.js @@ -4,6 +4,7 @@ import RepositoryService from 'consul-ui/services/repository'; import tomographyFactory from 'consul-ui/utils/tomography'; import distance from 'consul-ui/utils/distance'; const tomography = tomographyFactory(distance); +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'coordinate'; export default class CoordinateService extends RepositoryService { @@ -13,25 +14,21 @@ export default class CoordinateService extends RepositoryService { // Coordinates don't need nspaces so we have a custom method here // that doesn't accept nspaces - findAllByDatacenter(dc, configuration = {}) { - const query = { - dc: dc, - }; + @dataSource('/:ns/:dc/coordinates') + findAllByDatacenter(params, configuration = {}) { if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; - query.uri = configuration.uri; + params.index = configuration.cursor; + params.uri = configuration.uri; } - return this.store.query(this.getModelName(), query); + return this.store.query(this.getModelName(), params); } - findAllByNode(node, dc, configuration) { - return this.findAllByDatacenter(dc, configuration).then(function(coordinates) { + @dataSource('/:ns/:dc/coordinates/for-node/:id') + findAllByNode(params, configuration) { + return this.findAllByDatacenter(params, configuration).then(function(coordinates) { let results = {}; if (get(coordinates, 'length') > 1) { - results = tomography( - node, - coordinates - ); + results = tomography(params.id, coordinates); } results.meta = get(coordinates, 'meta'); return results; diff --git a/ui/packages/consul-ui/app/services/repository/dc.js b/ui/packages/consul-ui/app/services/repository/dc.js index cb0a3cd61..6eec243ef 100644 --- a/ui/packages/consul-ui/app/services/repository/dc.js +++ b/ui/packages/consul-ui/app/services/repository/dc.js @@ -2,6 +2,7 @@ import { inject as service } from '@ember/service'; import RepositoryService from 'consul-ui/services/repository'; import { get } from '@ember/object'; import Error from '@ember/error'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'dc'; export default class DcService extends RepositoryService { @@ -12,7 +13,8 @@ export default class DcService extends RepositoryService { return modelName; } - async findAll() { + @dataSource('/:ns/:dc/datacenters') + async findAll(params, configuration = {}) { return this.store.query(this.getModelName(), {}); } diff --git a/ui/packages/consul-ui/app/services/repository/discovery-chain.js b/ui/packages/consul-ui/app/services/repository/discovery-chain.js index cb7fd8894..2b1947674 100644 --- a/ui/packages/consul-ui/app/services/repository/discovery-chain.js +++ b/ui/packages/consul-ui/app/services/repository/discovery-chain.js @@ -1,6 +1,7 @@ import { inject as service } from '@ember/service'; import { get, set } from '@ember/object'; import RepositoryService from 'consul-ui/services/repository'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'discovery-chain'; const ERROR_MESH_DISABLED = 'Connect must be enabled in order to use this endpoint'; @@ -12,8 +13,9 @@ export default class DiscoveryChainService extends RepositoryService { return modelName; } - findBySlug(slug, dc, nspace, configuration = {}) { - const datacenter = this.dcs.peekOne(dc); + @dataSource('/:ns/:dc/discovery-chain/:id') + findBySlug(params, configuration = {}) { + const datacenter = this.dcs.peekOne(params.dc); if (datacenter !== null && !get(datacenter, 'MeshEnabled')) { return Promise.resolve(); } diff --git a/ui/packages/consul-ui/app/services/repository/intention.js b/ui/packages/consul-ui/app/services/repository/intention.js index d215797e1..e959e9aed 100644 --- a/ui/packages/consul-ui/app/services/repository/intention.js +++ b/ui/packages/consul-ui/app/services/repository/intention.js @@ -1,6 +1,7 @@ import { set, get } from '@ember/object'; import RepositoryService from 'consul-ui/services/repository'; import { PRIMARY_KEY } from 'consul-ui/models/intention'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'intention'; export default class IntentionRepository extends RepositoryService { @@ -34,13 +35,13 @@ export default class IntentionRepository extends RepositoryService { // legacy intentions are strange that in order to read/write you need access // to either/or the destination or source - async authorizeBySlug(cb, access, slug, dc, nspace) { - const [, source, , destination] = slug.split(':'); + async authorizeBySlug(cb, access, params) { + const [, source, , destination] = params.id.split(':'); const ability = this.permissions.abilityFor(this.getModelName()); - const permissions = ability + params.resources = ability .generateForSegment(source) .concat(ability.generateForSegment(destination)); - return this.authorizeByPermissions(cb, permissions, access, dc, nspace); + return this.authorizeByPermissions(cb, access, params); } async persist(obj) { @@ -56,11 +57,22 @@ export default class IntentionRepository extends RepositoryService { return res; } - async findByService(slug, dc, nspace, configuration = {}) { + @dataSource('/:ns/:dc/intentions') + async findAllByDatacenter() { + return super.findAllByDatacenter(...arguments); + } + + @dataSource('/:ns/:dc/intention/:id') + async findBySlug() { + return super.findBySlug(...arguments); + } + + @dataSource('/:ns/:dc/intentions/for-service/:id') + async findByService(params, configuration = {}) { const query = { - dc, - nspace, - filter: `SourceName == "${slug}" or DestinationName == "${slug}" or SourceName == "*" or DestinationName == "*"`, + dc: params.dc, + nspace: params.nspace, + filter: `SourceName == "${params.id}" or DestinationName == "${params.id}" or SourceName == "*" or DestinationName == "*"`, }; if (typeof configuration.cursor !== 'undefined') { query.index = configuration.cursor; diff --git a/ui/packages/consul-ui/app/services/repository/kv.js b/ui/packages/consul-ui/app/services/repository/kv.js index fadc4feed..145cb5e25 100644 --- a/ui/packages/consul-ui/app/services/repository/kv.js +++ b/ui/packages/consul-ui/app/services/repository/kv.js @@ -3,6 +3,7 @@ import isFolder from 'consul-ui/utils/isFolder'; import { get } from '@ember/object'; import { PRIMARY_KEY } from 'consul-ui/models/kv'; import { ACCESS_LIST } from 'consul-ui/abilities/base'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'kv'; export default class KvService extends RepositoryService { @@ -15,71 +16,65 @@ export default class KvService extends RepositoryService { } // this one gives you the full object so key,values and meta - async findBySlug(slug, dc, nspace, configuration = {}) { - if (isFolder(slug)) { + @dataSource('/:ns/:dc/kv/*id') + async findBySlug(params, configuration = {}) { + if (isFolder(params.id)) { // we only use findBySlug for a folder when we are looking to create a // parent for a key for retriveing something Model shaped. Therefore we // only use existing records or a fake record with the correct Key, - // which means we don't need to inpsect permissions as its an already + // which means we don't need to inspect permissions as its an already // existing KV or a fake one // TODO: This very much shouldn't be here, // needs to eventually use ember-datas generateId thing // in the meantime at least our fingerprinter - const id = JSON.stringify([nspace, dc, slug]); - let item = this.store.peekRecord(this.getModelName(), id); + const uid = JSON.stringify([params.ns, params.dc, params.id]); + let item = this.store.peekRecord(this.getModelName(), uid); if (!item) { item = this.create({ - Key: slug, - Datacenter: dc, - Namespace: nspace, + Key: params.id, + Datacenter: params.dc, + Namespace: params.ns, }); } return item; } else { - return super.findBySlug(slug, dc, nspace, configuration); + return super.findBySlug(...arguments); } } // this one only gives you keys // https://www.consul.io/api/kv.html - findAllBySlug(key, dc, nspace, configuration = {}) { - if (key === '/') { - key = ''; + findAllBySlug(params, configuration = {}) { + if (params.id === '/') { + params.id = ''; } return this.authorizeBySlug( async () => { - const query = { - id: key, - dc: dc, - ns: nspace, - separator: '/', - }; + params.separator = '/'; if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; + params.index = configuration.cursor; } let items; try { - items = await this.store.query(this.getModelName(), query); + items = await this.store.query(this.getModelName(), params); } catch (e) { if (get(e, 'errors.firstObject.status') === '404') { // TODO: This very much shouldn't be here, // needs to eventually use ember-datas generateId thing // in the meantime at least our fingerprinter - const id = JSON.stringify([dc, key]); - const record = this.store.peekRecord(this.getModelName(), id); + const uid = JSON.stringify([params.ns, params.dc, params.id]); + const record = this.store.peekRecord(this.getModelName(), uid); if (record) { record.unloadRecord(); } } throw e; } - return items.filter(item => key !== get(item, 'Key')); + return items.filter(item => params.id !== get(item, 'Key')); }, ACCESS_LIST, - key, - dc, - nspace + params ); } } diff --git a/ui/packages/consul-ui/app/services/repository/metrics.js b/ui/packages/consul-ui/app/services/repository/metrics.js index ab97ec488..f9079f6e5 100644 --- a/ui/packages/consul-ui/app/services/repository/metrics.js +++ b/ui/packages/consul-ui/app/services/repository/metrics.js @@ -1,9 +1,9 @@ import { inject as service } from '@ember/service'; import RepositoryService from 'consul-ui/services/repository'; +import dataSource from 'consul-ui/decorators/data-source'; // CONSUL_METRICS_POLL_INTERVAL controls how long between each poll to the // metrics provider - export default class MetricsService extends RepositoryService { @service('ui-config') config; @service('env') env; @@ -11,6 +11,10 @@ export default class MetricsService extends RepositoryService { error = null; + getModelName() { + return 'metrics'; + } + init() { super.init(...arguments); const config = this.config.get(); @@ -34,13 +38,26 @@ export default class MetricsService extends RepositoryService { } } - findServiceSummary(protocol, slug, dc, nspace, configuration = {}) { + @dataSource('/:ns/:dc/metrics/summary-for-service/:slug/:protocol') + findServiceSummary(params, configuration = {}) { if (this.error) { return Promise.reject(this.error); } const promises = [ - this.provider.serviceRecentSummarySeries(slug, dc, nspace, protocol, {}), - this.provider.serviceRecentSummaryStats(slug, dc, nspace, protocol, {}), + this.provider.serviceRecentSummarySeries( + params.slug, + params.dc, + params.ns, + params.protocol, + {} + ), + this.provider.serviceRecentSummaryStats( + params.slug, + params.dc, + params.ns, + params.protocol, + {} + ), ]; return Promise.all(promises).then(results => { return { @@ -53,27 +70,33 @@ export default class MetricsService extends RepositoryService { }); } - findUpstreamSummary(slug, dc, nspace, configuration = {}) { + @dataSource('/:ns/:dc/metrics/upstream-summary-for-service/:slug/:protocol') + findUpstreamSummary(params, configuration = {}) { if (this.error) { return Promise.reject(this.error); } - return this.provider.upstreamRecentSummaryStats(slug, dc, nspace, {}).then(result => { - result.meta = { - interval: this.env.var('CONSUL_METRICS_POLL_INTERVAL') || 10000, - }; - return result; - }); + return this.provider + .upstreamRecentSummaryStats(params.slug, params.dc, params.ns, {}) + .then(result => { + result.meta = { + interval: this.env.var('CONSUL_METRICS_POLL_INTERVAL') || 10000, + }; + return result; + }); } - findDownstreamSummary(slug, dc, nspace, configuration = {}) { + @dataSource('/:ns/:dc/metrics/downstream-summary-for-service/:slug/:protocol') + findDownstreamSummary(params, configuration = {}) { if (this.error) { return Promise.reject(this.error); } - return this.provider.downstreamRecentSummaryStats(slug, dc, nspace, {}).then(result => { - result.meta = { - interval: this.env.var('CONSUL_METRICS_POLL_INTERVAL') || 10000, - }; - return result; - }); + return this.provider + .downstreamRecentSummaryStats(params.slug, params.dc, params.ns, {}) + .then(result => { + result.meta = { + interval: this.env.var('CONSUL_METRICS_POLL_INTERVAL') || 10000, + }; + return result; + }); } } diff --git a/ui/packages/consul-ui/app/services/repository/node.js b/ui/packages/consul-ui/app/services/repository/node.js index 7b557f4ea..9c5e7cff8 100644 --- a/ui/packages/consul-ui/app/services/repository/node.js +++ b/ui/packages/consul-ui/app/services/repository/node.js @@ -1,4 +1,5 @@ import RepositoryService from 'consul-ui/services/repository'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'node'; export default class NodeService extends RepositoryService { @@ -6,13 +7,21 @@ export default class NodeService extends RepositoryService { return modelName; } - findLeader(dc, configuration = {}) { - const query = { - dc: dc, - }; + @dataSource('/:ns/:dc/nodes') + async findAllByDatacenter() { + return super.findAllByDatacenter(...arguments); + } + + @dataSource('/:ns/:dc/node/:id') + async findBySlug() { + return super.findBySlug(...arguments); + } + + @dataSource('/:ns/:dc/leader') + findLeader(params, configuration = {}) { if (typeof configuration.refresh !== 'undefined') { - query.uri = configuration.uri; + params.uri = configuration.uri; } - return this.store.queryLeader(this.getModelName(), query); + return this.store.queryLeader(this.getModelName(), params); } } diff --git a/ui/packages/consul-ui/app/services/repository/nspace.js b/ui/packages/consul-ui/app/services/repository/nspace.js index da962e2f9..b448288dd 100644 --- a/ui/packages/consul-ui/app/services/repository/nspace.js +++ b/ui/packages/consul-ui/app/services/repository/nspace.js @@ -1,5 +1,6 @@ import RepositoryService from 'consul-ui/services/repository'; import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/nspace'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'nspace'; export default class NspaceService extends RepositoryService { @@ -37,7 +38,13 @@ export default class NspaceService extends RepositoryService { return res; } - findAll(configuration = {}) { + @dataSource('/:ns/:dc/namespace/:id') + async findBySlug() { + return super.findBySlug(...arguments); + } + + @dataSource('/:ns/:dc/namespaces') + findAll(params, configuration = {}) { const query = {}; if (typeof configuration.cursor !== 'undefined') { query.index = configuration.cursor; diff --git a/ui/packages/consul-ui/app/services/repository/nspace/enabled.js b/ui/packages/consul-ui/app/services/repository/nspace/enabled.js index 8239c6346..2e385fd85 100644 --- a/ui/packages/consul-ui/app/services/repository/nspace/enabled.js +++ b/ui/packages/consul-ui/app/services/repository/nspace/enabled.js @@ -24,7 +24,7 @@ export default class EnabledService extends RepositoryService { return modelName; } - findAll(configuration = {}) { + findAll(params, configuration = {}) { const query = {}; if (typeof configuration.cursor !== 'undefined') { query.index = configuration.cursor; diff --git a/ui/packages/consul-ui/app/services/repository/oidc-provider.js b/ui/packages/consul-ui/app/services/repository/oidc-provider.js index 77907013c..6c5286924 100644 --- a/ui/packages/consul-ui/app/services/repository/oidc-provider.js +++ b/ui/packages/consul-ui/app/services/repository/oidc-provider.js @@ -2,6 +2,7 @@ import { inject as service } from '@ember/service'; import RepositoryService from 'consul-ui/services/repository'; import { getOwner } from '@ember/application'; import { set } from '@ember/object'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'oidc-provider'; const OAUTH_PROVIDER_NAME = 'oidc-with-url'; @@ -18,15 +19,19 @@ export default class OidcProviderService extends RepositoryService { return modelName; } - authorize(id, code, state, dc, nspace, configuration = {}) { - const query = { - id: id, - code: code, - state: state, - dc: dc, - ns: nspace, - }; - return this.store.authorize(this.getModelName(), query); + @dataSource('/:ns/:dc/oidc/providers') + async findAllByDatacenter() { + return super.findAllByDatacenter(...arguments); + } + + @dataSource('/:ns/:dc/oidc/provider/:id') + async findBySlug() { + return super.findBySlug(...arguments); + } + + @dataSource('/:ns/:dc/oidc/authorize/:id/:code/:state') + authorize(params, configuration = {}) { + return this.store.authorize(this.getModelName(), params); } logout(id, code, state, dc, nspace, configuration = {}) { diff --git a/ui/packages/consul-ui/app/services/repository/permission.js b/ui/packages/consul-ui/app/services/repository/permission.js index ff8a18ee5..b78b56ba5 100644 --- a/ui/packages/consul-ui/app/services/repository/permission.js +++ b/ui/packages/consul-ui/app/services/repository/permission.js @@ -97,31 +97,27 @@ export default class PermissionService extends RepositoryService { * If ACLs are disabled, then you have access to everything, hence we check * that here and only make the request if ACLs are enabled */ - async authorize(resources, dc, nspace) { + async authorize(params) { if (!this.env.var('CONSUL_ACLS_ENABLED')) { - return resources.map(item => { + return params.resources.map(item => { return { ...item, Allow: true, }; }); } else { - let permissions = []; + let resources = []; try { - permissions = await this.store.authorize('permission', { - dc: dc, - ns: nspace, - permissions: resources, - }); + resources = await this.store.authorize('permission', params); } catch (e) { runInDebug(() => console.error(e)); // passthrough } - return permissions; + return resources; } } - async findBySlug(segment, model, dc, nspace) { + async findBySlug(params, model) { let ability; try { ability = this._can.abilityFor(model); @@ -129,21 +125,23 @@ export default class PermissionService extends RepositoryService { return []; } - const resources = ability.generateForSegment(segment.toString()); + const resources = ability.generateForSegment(params.id.toString()); // if we get no resources for a segment it means that this // ability/permission isn't segmentable if (resources.length === 0) { return []; } - return this.authorize(resources, dc, nspace); + params.resources = resources; + return this.authorize(params); } - async findByPermissions(resources, dc, nspace) { - return this.authorize(resources, dc, nspace); + async findByPermissions(params) { + return this.authorize(params); } - async findAll(dc, nspace) { - this.permissions = await this.findByPermissions(REQUIRED_PERMISSIONS, dc, nspace); + async findAll(params) { + params.resources = REQUIRED_PERMISSIONS; + this.permissions = await this.findByPermissions(params); return this.permissions; } } diff --git a/ui/packages/consul-ui/app/services/repository/policy.js b/ui/packages/consul-ui/app/services/repository/policy.js index 57c4dedbe..694e9ceb9 100644 --- a/ui/packages/consul-ui/app/services/repository/policy.js +++ b/ui/packages/consul-ui/app/services/repository/policy.js @@ -3,6 +3,7 @@ import { get } from '@ember/object'; import statusFactory from 'consul-ui/utils/acls-status'; import isValidServerErrorFactory from 'consul-ui/utils/http/acl/is-valid-server-error'; import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/policy'; +import dataSource from 'consul-ui/decorators/data-source'; const isValidServerError = isValidServerErrorFactory(); const status = statusFactory(isValidServerError, Promise); @@ -21,6 +22,16 @@ export default class PolicyService extends RepositoryService { return SLUG_KEY; } + @dataSource('/:ns/:dc/policies') + async findAllByDatacenter() { + return super.findAllByDatacenter(...arguments); + } + + @dataSource('/:ns/:dc/policy/:id') + async findBySlug() { + return super.findBySlug(...arguments); + } + status(obj) { return status(obj); } diff --git a/ui/packages/consul-ui/app/services/repository/proxy.js b/ui/packages/consul-ui/app/services/repository/proxy.js index df0563afb..ea76d7e70 100644 --- a/ui/packages/consul-ui/app/services/repository/proxy.js +++ b/ui/packages/consul-ui/app/services/repository/proxy.js @@ -1,6 +1,8 @@ import RepositoryService from 'consul-ui/services/repository'; import { PRIMARY_KEY } from 'consul-ui/models/proxy'; import { get, set } from '@ember/object'; +import dataSource from 'consul-ui/decorators/data-source'; + const modelName = 'proxy'; export default class ProxyService extends RepositoryService { getModelName() { @@ -11,17 +13,13 @@ export default class ProxyService extends RepositoryService { return PRIMARY_KEY; } - findAllBySlug(slug, dc, nspace, configuration = {}) { - const query = { - id: slug, - ns: nspace, - dc: dc, - }; + @dataSource('/:ns/:dc/proxies/for-service/:id') + findAllBySlug(params, configuration = {}) { if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; - query.uri = configuration.uri; + params.index = configuration.cursor; + params.uri = configuration.uri; } - return this.store.query(this.getModelName(), query).then(items => { + return this.store.query(this.getModelName(), params).then(items => { items.forEach(item => { // swap out the id for the services id // so we can then assign the proxy to it if it exists @@ -37,17 +35,18 @@ export default class ProxyService extends RepositoryService { }); } - findInstanceBySlug(id, node, slug, dc, nspace, configuration) { - return this.findAllBySlug(slug, dc, nspace, configuration).then(function(items) { + @dataSource('/:ns/:dc/proxy-instance/:serviceId/:node/:id') + findInstanceBySlug(params, nspace, configuration) { + return this.findAllBySlug(params, configuration).then(function(items) { let res = {}; if (get(items, 'length') > 0) { let instance = items - .filterBy('ServiceProxy.DestinationServiceID', id) - .findBy('NodeName', node); + .filterBy('ServiceProxy.DestinationServiceID', params.serviceId) + .findBy('NodeName', params.node); if (instance) { res = instance; } else { - instance = items.findBy('ServiceProxy.DestinationServiceName', slug); + instance = items.findBy('ServiceProxy.DestinationServiceName', params.id); if (instance) { res = instance; } diff --git a/ui/packages/consul-ui/app/services/repository/role.js b/ui/packages/consul-ui/app/services/repository/role.js index 6c40c3929..32b17aac4 100644 --- a/ui/packages/consul-ui/app/services/repository/role.js +++ b/ui/packages/consul-ui/app/services/repository/role.js @@ -2,6 +2,7 @@ import RepositoryService from 'consul-ui/services/repository'; import statusFactory from 'consul-ui/utils/acls-status'; import isValidServerErrorFactory from 'consul-ui/utils/http/acl/is-valid-server-error'; import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/role'; +import dataSource from 'consul-ui/decorators/data-source'; const isValidServerError = isValidServerErrorFactory(); const status = statusFactory(isValidServerError, Promise); @@ -20,6 +21,16 @@ export default class RoleService extends RepositoryService { return SLUG_KEY; } + @dataSource('/:ns/:dc/roles') + async findAllByDatacenter() { + return super.findAllByDatacenter(...arguments); + } + + @dataSource('/:ns/:dc/role/:id') + async findBySlug() { + return super.findBySlug(...arguments); + } + status(obj) { return status(obj); } diff --git a/ui/packages/consul-ui/app/services/repository/service-instance.js b/ui/packages/consul-ui/app/services/repository/service-instance.js index ec604d38e..5d4ede9c7 100644 --- a/ui/packages/consul-ui/app/services/repository/service-instance.js +++ b/ui/packages/consul-ui/app/services/repository/service-instance.js @@ -2,6 +2,7 @@ import RepositoryService from 'consul-ui/services/repository'; import { inject as service } from '@ember/service'; import { set } from '@ember/object'; import { ACCESS_READ } from 'consul-ui/abilities/base'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'service-instance'; export default class ServiceInstanceService extends RepositoryService { @@ -10,47 +11,34 @@ export default class ServiceInstanceService extends RepositoryService { return modelName; } - async findByService(slug, dc, nspace, configuration = {}) { - const query = { - dc: dc, - ns: nspace, - id: slug, - }; + @dataSource('/:ns/:dc/service-instances/for-service/:id') + async findByService(params, configuration = {}) { if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; - query.uri = configuration.uri; + params.index = configuration.cursor; + params.uri = configuration.uri; } return this.authorizeBySlug( - async () => this.store.query(this.getModelName(), query), + async () => this.store.query(this.getModelName(), params), ACCESS_READ, - slug, - dc, - nspace + params ); } - async findBySlug(serviceId, node, service, dc, nspace, configuration = {}) { - const query = { - dc: dc, - ns: nspace, - serviceId: serviceId, - node: node, - id: service, - }; + @dataSource('/:ns/:dc/service-instance/:serviceId/:node/:id') + async findBySlug(params, configuration = {}) { if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; - query.uri = configuration.uri; + params.index = configuration.cursor; + params.uri = configuration.uri; } return this.authorizeBySlug( - async () => this.store.queryRecord(this.getModelName(), query), + async () => this.store.queryRecord(this.getModelName(), params), ACCESS_READ, - service, - dc, - nspace + params ); } - async findProxyBySlug(serviceId, node, service, dc, nspace, configuration = {}) { + @dataSource('/:ns/:dc/proxy-service-instance/:serviceId/:node/:id') + async findProxyBySlug(params, configuration = {}) { const instance = await this.findBySlug(...arguments); let proxy = this.store.peekRecord('proxy', instance.uid); // Currently, we call the proxy endpoint before this endpoint diff --git a/ui/packages/consul-ui/app/services/repository/service.js b/ui/packages/consul-ui/app/services/repository/service.js index b804b486b..4ada89702 100644 --- a/ui/packages/consul-ui/app/services/repository/service.js +++ b/ui/packages/consul-ui/app/services/repository/service.js @@ -1,20 +1,23 @@ import RepositoryService from 'consul-ui/services/repository'; +import dataSource from 'consul-ui/decorators/data-source'; + const modelName = 'service'; -export default class _RepositoryService extends RepositoryService { +export default class ServiceService extends RepositoryService { getModelName() { return modelName; } - findGatewayBySlug(slug, dc, nspace, configuration = {}) { - const query = { - dc: dc, - ns: nspace, - gateway: slug, - }; + @dataSource('/:ns/:dc/services') + async findAllByDatacenter() { + return super.findAllByDatacenter(...arguments); + } + + @dataSource('/:ns/:dc/gateways/for-service/:gateway') + findGatewayBySlug(params, configuration = {}) { if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; - query.uri = configuration.uri; + params.index = configuration.cursor; + params.uri = configuration.uri; } - return this.store.query(this.getModelName(), query); + return this.store.query(this.getModelName(), params); } } diff --git a/ui/packages/consul-ui/app/services/repository/session.js b/ui/packages/consul-ui/app/services/repository/session.js index bc98df92d..8534232cd 100644 --- a/ui/packages/consul-ui/app/services/repository/session.js +++ b/ui/packages/consul-ui/app/services/repository/session.js @@ -1,5 +1,6 @@ import { inject as service } from '@ember/service'; import RepositoryService from 'consul-ui/services/repository'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'session'; export default class SessionService extends RepositoryService { @@ -10,21 +11,18 @@ export default class SessionService extends RepositoryService { return modelName; } - findByNode(node, dc, nspace, configuration = {}) { - const query = { - id: node, - dc: dc, - ns: nspace, - }; + @dataSource('/:ns/:dc/sessions/for-node/:id') + findByNode(params, configuration = {}) { if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; - query.uri = configuration.uri; + params.index = configuration.cursor; + params.uri = configuration.uri; } - return this.store.query(this.getModelName(), query); + return this.store.query(this.getModelName(), params); } // TODO: Why Key? Probably should be findBySlug like the others - findByKey(slug, dc, nspace) { + @dataSource('/:ns/:dc/sessions/for-key/:id') + findByKey(params, configuration = {}) { return this.findBySlug(...arguments); } } diff --git a/ui/packages/consul-ui/app/services/repository/token.js b/ui/packages/consul-ui/app/services/repository/token.js index 75f4017d6..08987e2bd 100644 --- a/ui/packages/consul-ui/app/services/repository/token.js +++ b/ui/packages/consul-ui/app/services/repository/token.js @@ -3,6 +3,7 @@ import { get } from '@ember/object'; import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/token'; import statusFactory from 'consul-ui/utils/acls-status'; import isValidServerErrorFactory from 'consul-ui/utils/http/acl/is-valid-server-error'; +import dataSource from 'consul-ui/decorators/data-source'; const isValidServerError = isValidServerErrorFactory(); const status = statusFactory(isValidServerError, Promise); @@ -25,11 +26,13 @@ export default class TokenService extends RepositoryService { return status(obj); } - self(secret, dc) { + @dataSource('/:ns/:dc/token/self/:secret') + self(params) { + // TODO: Does this need ns passing through? return this.store .self(this.getModelName(), { - secret: secret, - dc: dc, + secret: params.secret, + dc: params.dc, }) .catch(e => { // If we get this 500 RPC error, it means we are a legacy ACL cluster @@ -37,7 +40,7 @@ export default class TokenService extends RepositoryService { if (isValidServerError(e)) { return { AccessorID: null, - SecretID: secret, + SecretID: params.secret, }; } return Promise.reject(e); @@ -48,19 +51,19 @@ export default class TokenService extends RepositoryService { return this.store.clone(this.getModelName(), get(item, PRIMARY_KEY)); } - findByPolicy(id, dc, nspace) { + findByPolicy(params) { return this.store.query(this.getModelName(), { - policy: id, - dc: dc, - ns: nspace, + policy: params.id, + dc: params.dc, + ns: params.ns, }); } - findByRole(id, dc, nspace) { + findByRole(params) { return this.store.query(this.getModelName(), { - role: id, - dc: dc, - ns: nspace, + role: params.id, + dc: params.dc, + ns: params.ns, }); } } diff --git a/ui/packages/consul-ui/app/services/repository/topology.js b/ui/packages/consul-ui/app/services/repository/topology.js index bd9fb0309..74b6df5e8 100644 --- a/ui/packages/consul-ui/app/services/repository/topology.js +++ b/ui/packages/consul-ui/app/services/repository/topology.js @@ -1,6 +1,7 @@ import { inject as service } from '@ember/service'; import RepositoryService from 'consul-ui/services/repository'; import { get, set } from '@ember/object'; +import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'topology'; const ERROR_MESH_DISABLED = 'Connect must be enabled in order to use this endpoint'; @@ -13,24 +14,19 @@ export default class TopologyService extends RepositoryService { return modelName; } - findBySlug(slug, kind, dc, nspace, configuration = {}) { - const datacenter = this.dcs.peekOne(dc); + @dataSource('/:ns/:dc/topology/:id/:kind') + findBySlug(params, configuration = {}) { + const datacenter = this.dcs.peekOne(params.dc); if (datacenter !== null && !get(datacenter, 'MeshEnabled')) { return Promise.resolve(); } - const query = { - dc: dc, - ns: nspace, - id: slug, - kind: kind, - }; if (typeof configuration.cursor !== 'undefined') { - query.index = configuration.cursor; - query.uri = configuration.uri; + params.index = configuration.cursor; + params.uri = configuration.uri; } - return this.store.queryRecord(this.getModelName(), query).catch(e => { + return this.store.queryRecord(this.getModelName(), params).catch(e => { const code = get(e, 'errors.firstObject.status'); - const body = get(e, 'errors.firstObject.detail').trim(); + const body = (get(e, 'errors.firstObject.detail') || '').trim(); switch (code) { case '500': if (datacenter !== null && body.endsWith(ERROR_MESH_DISABLED)) { diff --git a/ui/packages/consul-ui/package.json b/ui/packages/consul-ui/package.json index f29d858e7..957f1164c 100644 --- a/ui/packages/consul-ui/package.json +++ b/ui/packages/consul-ui/package.json @@ -160,7 +160,8 @@ "tape": "^5.0.1", "text-encoding": "^0.7.0", "tippy.js": "^6.2.7", - "torii": "^0.10.1" + "torii": "^0.10.1", + "wayfarer": "^7.0.1" }, "resolutions": { "ember-basic-dropdown": "^3.0.10" diff --git a/ui/packages/consul-ui/tests/integration/services/repository/acl-test.js b/ui/packages/consul-ui/tests/integration/services/repository/acl-test.js index 5883ab4f1..bf91f9168 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/acl-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/acl-test.js @@ -19,7 +19,7 @@ test('findByDatacenter returns the correct data for list endpoint', function(ass }); }, function performTest(service) { - return service.findAllByDatacenter(dc); + return service.findAllByDatacenter({ dc }); }, function performAssertion(actual, expected) { assert.deepEqual( @@ -47,7 +47,7 @@ test('findBySlug returns the correct data for item endpoint', function(assert) { return stub(`/v1/acl/info/${id}?dc=${dc}`); }, function performTest(service) { - return service.findBySlug(id, dc); + return service.findBySlug({ id, dc }); }, function performAssertion(actual, expected) { assert.deepEqual( diff --git a/ui/packages/consul-ui/tests/integration/services/repository/auth-method-test.js b/ui/packages/consul-ui/tests/integration/services/repository/auth-method-test.js index 2d489be3a..966128d0b 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/auth-method-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/auth-method-test.js @@ -25,7 +25,10 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findAllByDatacenter(dc, nspace || undefinedNspace); + return service.findAllByDatacenter({ + dc: dc, + nspace: nspace || undefinedNspace, + }); }, function performAssertion(actual, expected) { assert.deepEqual( diff --git a/ui/packages/consul-ui/tests/integration/services/repository/coordinate-test.js b/ui/packages/consul-ui/tests/integration/services/repository/coordinate-test.js index cdb390b67..f46db8168 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/coordinate-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/coordinate-test.js @@ -24,7 +24,7 @@ test('findAllByDatacenter returns the correct data for list endpoint', function( }); }, function performTest(service) { - return service.findAllByDatacenter(dc); + return service.findAllByDatacenter({ dc }); }, function performAssertion(actual, expected) { assert.deepEqual( @@ -51,11 +51,11 @@ test('findAllByNode calls findAllByDatacenter with the correct arguments', funct cursor: 1, }; const service = this.subject(); - service.findAllByDatacenter = function(dc, configuration) { + service.findAllByDatacenter = function(params, configuration) { assert.equal(arguments.length, 2, 'Expected to be called with the correct number of arguments'); - assert.equal(dc, datacenter); + assert.equal(params.dc, datacenter); assert.deepEqual(configuration, conf); return Promise.resolve([]); }; - return service.findAllByNode('node-name', datacenter, conf); + return service.findAllByNode({ node: 'node-name', dc: datacenter }, conf); }); diff --git a/ui/packages/consul-ui/tests/integration/services/repository/discovery-chain-test.js b/ui/packages/consul-ui/tests/integration/services/repository/discovery-chain-test.js index 9b5ea1e89..6333c06d9 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/discovery-chain-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/discovery-chain-test.js @@ -18,7 +18,7 @@ test('findBySlug returns the correct data for item endpoint', function(assert) { }); }, function performTest(service) { - return service.findBySlug(id, dc); + return service.findBySlug({ id, dc }); }, function performAssertion(actual, expected) { const result = expected(function(payload) { diff --git a/ui/packages/consul-ui/tests/integration/services/repository/kv-test.js b/ui/packages/consul-ui/tests/integration/services/repository/kv-test.js index 98bd9b55a..d7810aeac 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/kv-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/kv-test.js @@ -23,7 +23,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findAllBySlug(id, dc, nspace || undefinedNspace); + return service.findAllBySlug({ id, dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( @@ -51,7 +51,7 @@ const undefinedNspace = 'default'; return stub(`/v1/kv/${id}?dc=${dc}${typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``}`); }, function(service) { - return service.findBySlug(id, dc, nspace || undefinedNspace); + return service.findBySlug({ id, dc, ns: nspace || undefinedNspace }); }, function(actual, expected) { assert.deepEqual( diff --git a/ui/packages/consul-ui/tests/integration/services/repository/node-test.js b/ui/packages/consul-ui/tests/integration/services/repository/node-test.js index a7a1f5d67..edd5899f9 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/node-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/node-test.js @@ -25,7 +25,7 @@ test('findByDatacenter returns the correct data for list endpoint', function(ass }); }, function performTest(service) { - return service.findAllByDatacenter(dc); + return service.findAllByDatacenter({ dc }); }, function performAssertion(actual, expected) { actual.forEach(item => { @@ -44,7 +44,7 @@ test('findBySlug returns the correct data for item endpoint', function(assert) { return stub(`/v1/internal/ui/node/${id}?dc=${dc}`); }, function(service) { - return service.findBySlug(id, dc); + return service.findBySlug({ id, dc }); }, function(actual, expected) { assert.equal(actual.uid, `["${nspace}","${dc}","${actual.ID}"]`); diff --git a/ui/packages/consul-ui/tests/integration/services/repository/policy-test.js b/ui/packages/consul-ui/tests/integration/services/repository/policy-test.js index 287db1739..3b25fd954 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/policy-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/policy-test.js @@ -29,7 +29,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findAllByDatacenter(dc, nspace || undefinedNspace); + return service.findAllByDatacenter({ dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( @@ -59,7 +59,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findBySlug(id, dc, nspace || undefinedNspace); + return service.findBySlug({ id, dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( diff --git a/ui/packages/consul-ui/tests/integration/services/repository/role-test.js b/ui/packages/consul-ui/tests/integration/services/repository/role-test.js index d3dc26084..197b30a23 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/role-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/role-test.js @@ -30,7 +30,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findAllByDatacenter(dc, nspace || undefinedNspace); + return service.findAllByDatacenter({ dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( @@ -61,7 +61,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findBySlug(id, dc, nspace || undefinedNspace); + return service.findBySlug({ id, dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( diff --git a/ui/packages/consul-ui/tests/integration/services/repository/service-test.js b/ui/packages/consul-ui/tests/integration/services/repository/service-test.js index 39eea2a7e..586c9d8dd 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/service-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/service-test.js @@ -33,7 +33,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findGatewayBySlug(gateway, dc, nspace || undefinedNspace, conf); + return service.findGatewayBySlug({ gateway, dc, ns: nspace || undefinedNspace }, conf); }, function performAssertion(actual, expected) { const result = expected(function(payload) { diff --git a/ui/packages/consul-ui/tests/integration/services/repository/session-test.js b/ui/packages/consul-ui/tests/integration/services/repository/session-test.js index 300252a31..d5f199d1f 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/session-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/session-test.js @@ -29,7 +29,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findByNode(id, dc, nspace || undefinedNspace); + return service.findByNode({ id, dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( @@ -59,7 +59,7 @@ const undefinedNspace = 'default'; ); }, function(service) { - return service.findByKey(id, dc, nspace || undefinedNspace); + return service.findByKey({ id, dc, ns: nspace || undefinedNspace }); }, function(actual, expected) { assert.deepEqual( diff --git a/ui/packages/consul-ui/tests/integration/services/repository/token-test.js b/ui/packages/consul-ui/tests/integration/services/repository/token-test.js index 8d26cc39c..864adf6fa 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/token-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/token-test.js @@ -26,7 +26,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findAllByDatacenter(dc, nspace || undefinedNspace); + return service.findAllByDatacenter({ dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( @@ -57,7 +57,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findBySlug(id, dc, nspace || undefinedNspace); + return service.findBySlug({ id, dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( @@ -99,7 +99,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findByPolicy(policy, dc, nspace || undefinedNspace); + return service.findByPolicy({ id: policy, dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( @@ -136,7 +136,7 @@ const undefinedNspace = 'default'; ); }, function performTest(service) { - return service.findByRole(role, dc, nspace || undefinedNspace); + return service.findByRole({ id: role, dc, ns: nspace || undefinedNspace }); }, function performAssertion(actual, expected) { assert.deepEqual( diff --git a/ui/packages/consul-ui/tests/integration/services/repository/topology-test.js b/ui/packages/consul-ui/tests/integration/services/repository/topology-test.js index 45098469e..7ab7af665 100644 --- a/ui/packages/consul-ui/tests/integration/services/repository/topology-test.js +++ b/ui/packages/consul-ui/tests/integration/services/repository/topology-test.js @@ -19,7 +19,7 @@ test('findBySlug returns the correct data for item endpoint', function(assert) { }); }, function performTest(service) { - return service.findBySlug(id, kind, dc); + return service.findBySlug({ id, kind, dc }); }, function performAssertion(actual, expected) { const result = expected(function(payload) { diff --git a/ui/yarn.lock b/ui/yarn.lock index e102d55ac..411468edb 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -13365,6 +13365,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== +nanoassert@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-1.1.0.tgz#4f3152e09540fde28c76f44b19bbcd1d5a42478d" + integrity sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40= + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -17877,6 +17882,13 @@ watchpack@^1.5.0, watchpack@^1.7.4: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.0" +wayfarer@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/wayfarer/-/wayfarer-7.0.1.tgz#17a64d351d49f9d3d6c508155867df7658184ce3" + integrity sha512-yf+kAlOYnJRjLxflLy+1+xEclb6222EAVvAjSY+Yz2qAIDrXeN5wLl/G302Mwv3E0KMg1HT/WDGsvSymX0U7Rw== + dependencies: + nanoassert "^1.1.0" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"