From 4ab7304411562ca44006a5f60c09a779e577cfa7 Mon Sep 17 00:00:00 2001 From: Zhihe Li Date: Tue, 9 Aug 2022 16:12:56 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Added=20list=20view=20fo?= =?UTF-8?q?r=20workers=20(#1234)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 Add list view for workers ✅ Closes: ICU-5251 --- addons/api/addon/models/worker.js | 19 +++- addons/api/addon/serializers/worker.js | 14 ++- addons/api/mirage/config.js | 5 +- addons/api/mirage/factories/worker.js | 9 +- .../api/mirage/generated/factories/worker.js | 2 +- addons/core/translations/form/en-us.yaml | 2 + addons/core/translations/resources/en-us.yaml | 3 + ui/admin/app/router.js | 1 + ui/admin/app/routes/scopes/scope/workers.js | 9 ++ .../app/routes/scopes/scope/workers/index.js | 50 ++++++++ .../app/routes/scopes/scope/workers/worker.js | 15 +++ .../scopes/scope/workers/worker/index.js | 3 + .../templates/scopes/scope/workers/index.hbs | 107 +++++++++++++++++- .../templates/scopes/scope/workers/worker.hbs | 3 + .../scopes/scope/workers/worker/index.hbs | 2 + .../tests/acceptance/workers/list-test.js | 73 ++++++++++++ .../scopes/scope/workers/worker-test.js | 11 ++ .../scopes/scope/workers/worker/index-test.js | 11 ++ 18 files changed, 324 insertions(+), 15 deletions(-) create mode 100644 ui/admin/app/routes/scopes/scope/workers/worker.js create mode 100644 ui/admin/app/routes/scopes/scope/workers/worker/index.js create mode 100644 ui/admin/app/templates/scopes/scope/workers/worker.hbs create mode 100644 ui/admin/app/templates/scopes/scope/workers/worker/index.hbs create mode 100644 ui/admin/tests/acceptance/workers/list-test.js create mode 100644 ui/admin/tests/unit/routes/scopes/scope/workers/worker-test.js create mode 100644 ui/admin/tests/unit/routes/scopes/scope/workers/worker/index-test.js diff --git a/addons/api/addon/models/worker.js b/addons/api/addon/models/worker.js index 63f4690d2..e08909020 100644 --- a/addons/api/addon/models/worker.js +++ b/addons/api/addon/models/worker.js @@ -4,6 +4,23 @@ import { attr } from '@ember-data/model'; export default class WorkerModel extends GeneratedWorkerModel { // =attributes + /** + * Names are optional on models in our API. Thus we need to fallback on ID + * for display purposes. + * @type {string} + */ + get displayName() { + return this.name || this.id; + } + + /** + * Returns whether the worker is a pki worker. + * @type {boolean} + */ + get isPki() { + return this.type === 'pki'; + } + @attr({ description: 'The deduplicated union of the tags reported by the worker ' + @@ -28,7 +45,7 @@ export default class WorkerModel extends GeneratedWorkerModel { addWorkerLed(workerGeneratedAuthToken, options = { adapterOptions: {} }) { const defaultAdapterOptions = { method: 'create:worker-led', - workerGeneratedAuthToken + workerGeneratedAuthToken, }; return super.save({ ...options, diff --git a/addons/api/addon/serializers/worker.js b/addons/api/addon/serializers/worker.js index cacd19876..54f8099a2 100644 --- a/addons/api/addon/serializers/worker.js +++ b/addons/api/addon/serializers/worker.js @@ -1,7 +1,7 @@ import ApplicationSerializer from './application'; export default class WorkerSerializer extends ApplicationSerializer { - // =methods + // =methods /** * If `adapterOptions.workerGeneratedAuthToken` is set, @@ -10,11 +10,15 @@ export default class WorkerSerializer extends ApplicationSerializer { * @param {Snapshot} snapshot * @return {object} */ - serialize(snapshot) { + serialize(snapshot) { let serialized = super.serialize(...arguments); - const workerGeneratedAuthToken = snapshot?.adapterOptions?.workerGeneratedAuthToken; + const workerGeneratedAuthToken = + snapshot?.adapterOptions?.workerGeneratedAuthToken; if (workerGeneratedAuthToken) - serialized = this.serializeWithGeneratedToken(snapshot, workerGeneratedAuthToken); + serialized = this.serializeWithGeneratedToken( + snapshot, + workerGeneratedAuthToken + ); return serialized; } @@ -27,7 +31,7 @@ export default class WorkerSerializer extends ApplicationSerializer { serializeWithGeneratedToken(snapshot, workerGeneratedAuthToken) { return { version: snapshot.attr('version'), - worker_generated_auth_token: workerGeneratedAuthToken + worker_generated_auth_token: workerGeneratedAuthToken, }; } } diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index 166ba8f74..1dabfcd90 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -642,8 +642,9 @@ export default function () { return workers.where({ scopeId }); } ); - this.get('/worker/:id'); - this.patch('/worker/:id'); + this.get('/workers/:id'); + this.del('/workers/:id'); + this.patch('/workers/:id'); /* Uncomment the following line and the Response import above * Then change the response code to simulate error responses. diff --git a/addons/api/mirage/factories/worker.js b/addons/api/mirage/factories/worker.js index 6344dbcd6..5f334b5ed 100644 --- a/addons/api/mirage/factories/worker.js +++ b/addons/api/mirage/factories/worker.js @@ -4,7 +4,6 @@ import generateId from '../helpers/id'; import { faker } from '@faker-js/faker'; const types = ['pki', 'kms']; -const tags = ['dev', faker.random.word(), faker.random.word()]; export default factory.extend({ id: () => generateId('w_'), @@ -16,10 +15,10 @@ export default factory.extend({ 'delete', ], type: (i) => types[i % types.length], - canonical_tags: () => ({ - type: tags, + canonical_tags: (i) => ({ + type: i % 2 === 0 ? ['dev', faker.random.word(), faker.random.word()] : [], }), - config_tags: () => ({ - type: tags, + config_tags: (i) => ({ + type: i % 2 === 0 ? ['dev', faker.random.word(), faker.random.word()] : [], }), }); diff --git a/addons/api/mirage/generated/factories/worker.js b/addons/api/mirage/generated/factories/worker.js index d2bb534eb..7ce830569 100644 --- a/addons/api/mirage/generated/factories/worker.js +++ b/addons/api/mirage/generated/factories/worker.js @@ -3,7 +3,7 @@ import { faker } from '@faker-js/faker'; /** * GeneratedWorkerModelFactory - * User contains all fields related to a Worker resource + * Worker contains all fields related to a Worker resource */ export default Factory.extend({ name: () => faker.random.words(), diff --git a/addons/core/translations/form/en-us.yaml b/addons/core/translations/form/en-us.yaml index 1132b4edc..6702191b3 100644 --- a/addons/core/translations/form/en-us.yaml +++ b/addons/core/translations/form/en-us.yaml @@ -149,3 +149,5 @@ ip_addresses: sync-interval: label: Sync Interval help: Number of seconds between the time Boundary syncs hosts in this set. +tags: + label: Tags diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index fcc6e664d..8836135ae 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -423,3 +423,6 @@ worker: messages: welcome: title: Welcome to Workers + table: + session_count: Session Count + ip_address: IP Address diff --git a/ui/admin/app/router.js b/ui/admin/app/router.js index ec9218e47..8fb944870 100644 --- a/ui/admin/app/router.js +++ b/ui/admin/app/router.js @@ -127,6 +127,7 @@ Router.map(function () { }); this.route('workers', function () { this.route('new'); + this.route('worker', { path: ':worker_id' }, function () {}); }); }); }); diff --git a/ui/admin/app/routes/scopes/scope/workers.js b/ui/admin/app/routes/scopes/scope/workers.js index 0c8b821a3..f59453172 100644 --- a/ui/admin/app/routes/scopes/scope/workers.js +++ b/ui/admin/app/routes/scopes/scope/workers.js @@ -1,5 +1,6 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; export default class ScopesScopeWorkersRoute extends Route { // =services @@ -28,4 +29,12 @@ export default class ScopesScopeWorkersRoute extends Route { return this.store.query('worker', { scope_id }); } } + + /** + * Refreshes worker data. + */ + @action + refreshWorkers() { + return super.refresh(...arguments); + } } diff --git a/ui/admin/app/routes/scopes/scope/workers/index.js b/ui/admin/app/routes/scopes/scope/workers/index.js index 856802b1c..6d65b459b 100644 --- a/ui/admin/app/routes/scopes/scope/workers/index.js +++ b/ui/admin/app/routes/scopes/scope/workers/index.js @@ -1,8 +1,58 @@ import Route from '@ember/routing/route'; +import { action } from '@ember/object'; +import { resourceFilter } from 'core/decorators/resource-filter'; export default class ScopesScopeWorkersIndexRoute extends Route { + // =attributes + + @resourceFilter({ + allowed: (route) => { + const configTags = route + .modelFor('scopes.scope.workers') + .toArray() + .flatMap((worker) => worker.config_tags.type); + + // Filter out duplicate tags + return [...new Set(configTags)]; + }, + }) + tags; + // =methods + model() { + const workers = this.modelFor('scopes.scope.workers'); + + if (this.tags?.length) { + // Return workers that have config tags that have at + // least one intersection with the filter tags + return workers.filter( + (worker) => + worker.config_tags.type.filter(Set.prototype.has, new Set(this.tags)) + .length > 0 + ); + } + return workers; + } + + /** + * Clears filter selections. + */ + @action + clearAllFilters() { + this.tags = []; + } + + /** + * Sets the specified resource filter field to the specified value. + * @param {string} field + * @param value + */ + @action + filterBy(field, value) { + this[field] = value; + } + setupController(controller) { const scope = this.modelFor('scopes.scope'); super.setupController(...arguments); diff --git a/ui/admin/app/routes/scopes/scope/workers/worker.js b/ui/admin/app/routes/scopes/scope/workers/worker.js new file mode 100644 index 000000000..2b3cba8c8 --- /dev/null +++ b/ui/admin/app/routes/scopes/scope/workers/worker.js @@ -0,0 +1,15 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeWorkersWorkerRoute extends Route { + // =methods + + /** + * Load a worker in current scope. + * @param {object} params + * @param {string} params.worker_id + * @return {WorkerModel} + */ + model({ worker_id }) { + return this.store.findRecord('worker', worker_id); + } +} diff --git a/ui/admin/app/routes/scopes/scope/workers/worker/index.js b/ui/admin/app/routes/scopes/scope/workers/worker/index.js new file mode 100644 index 000000000..078a942b6 --- /dev/null +++ b/ui/admin/app/routes/scopes/scope/workers/worker/index.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeWorkersWorkerIndexRoute extends Route {} diff --git a/ui/admin/app/templates/scopes/scope/workers/index.hbs b/ui/admin/app/templates/scopes/scope/workers/index.hbs index 8b3168d9a..f750cb703 100644 --- a/ui/admin/app/templates/scopes/scope/workers/index.hbs +++ b/ui/admin/app/templates/scopes/scope/workers/index.hbs @@ -23,7 +23,112 @@ {{#if @model}} - {{! TODO: List view }} + + + + {{#with + (resource-filter 'scopes.scope.workers.index' 'tags') + as |filter| + }} + + + + + + + + {{/with}} + + {{#if + (has-resource-filter-selections + 'scopes.scope.workers.index' 'tags' + ) + }} + {{t 'actions.clear-all-filters'}} + + {{/if}} + + + + + + + + + + + + + {{t 'resources.worker.title'}} + {{t 'form.id.label'}} + {{t 'form.type.label'}} + {{t + 'resources.worker.table.session_count' + }} + {{t + 'resources.worker.table.ip_address' + }} + + + + {{#each @model as |worker|}} + + + {{#if (and (can 'read worker' worker) worker.isPki)}} + + {{worker.displayName}} + + {{else}} + {{worker.displayName}} + {{/if}} + {{#if worker.description}} +

{{worker.description}}

+ {{/if}} + {{#if worker.config_tags.type}} +
+ {{#each worker.config_tags.type as |tag|}} + {{tag}} + {{/each}} +
+ {{/if}} +
+ + + {{worker.id}} + + + {{worker.type}} + {{worker.active_connection_count}} + {{worker.address}} +
+ {{/each}} +
+
{{else}}