Icu 5245 add workers tab to navigation and initial overview (#1229)

* feat: 🎸 Add initial workers UI navigation page

✅ Closes: ICU-5245
This commit is contained in:
Zhihe Li authored and GitHub committed 2022-08-01 16:04:41 -04:00
1 parent bc6f849270
commit 96b0ae3b06
24 files changed
+275 -7

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
import ModelAbility from './model';
/**
* Provides abilities for workers.
*/
export default class WorkerAbility extends ModelAbility {
// =permissions
/**
* @type {boolean}
*/
get canCreateWorkerLed() {
return this.hasAuthorizedCollectionAction('create:worker-led');
}
}
+1
View File
@@ -0,0 +1 @@
export { default } from 'api/abilities/worker';
+9 -2
View File
@@ -14,8 +14,8 @@ export default factory.extend({
'delete',
],
authorized_collection_actions: () => {
return {
authorized_collection_actions() {
const collectionActions = {
scopes: ['create', 'list'],
users: ['create', 'list'],
sessions: ['list'],
@@ -26,6 +26,13 @@ export default factory.extend({
'auth-methods': ['create', 'list'],
'host-catalogs': ['create', 'list'],
};
// Worker permissions only available on the global scope
if (this.type === 'global') {
collectionActions.workers = ['create:worker-led', 'list'];
}
return collectionActions;
},
id: () => generateId('s_'),
+1 -1
View File
@@ -1,4 +1,4 @@
import factory from '../generated/factories/user';
import factory from '../generated/factories/worker';
import permissions from '../helpers/permissions';
import generateId from '../helpers/id';
import { faker } from '@faker-js/faker';
@@ -2,8 +2,8 @@ import { Factory } from 'ember-cli-mirage';
import { faker } from '@faker-js/faker';
/**
* GeneratedUserModelFactory
* User contains all fields related to a User resource
* GeneratedWorkerModelFactory
* User contains all fields related to a Worker resource
*/
export default Factory.extend({
name: () => faker.random.words(),
@@ -11,7 +11,7 @@ export default Factory.extend({
created_time: () => faker.date.recent(),
updated_time: () => faker.date.recent(),
version: () => faker.datatype.number(),
address: () => `${faker.internet.ipv6()}${faker.internet.port()}`,
address: () => `${faker.internet.ipv4()}:${faker.internet.port()}`,
last_status_time: () => faker.date.recent(),
active_connection_count: () => faker.datatype.number(),
active_connection_count: () => faker.datatype.number({ max: 10 }),
});
+3
View File
@@ -65,4 +65,7 @@ export default function (server) {
server.createList('group', 3, { scope });
server.createList('role', 3, { scope });
});
// Workers
server.createList('worker', 3, { scope: globalScope });
}
@@ -412,3 +412,10 @@ managed-group:
messages:
title: Welcome to Members
description: No members in this managed group.
worker:
title: Worker
title_plural: Workers
description: Workers are the server components that perform the session handling.
messages:
welcome:
title: Welcome to Workers
+12
View File
@@ -0,0 +1,12 @@
import WorkerAbility from 'api/abilities/worker';
export default class OverrideWorkerAbility extends WorkerAbility {
/**
* Navigating to workers is allowed if either list or create grants
* are present.
* @type {boolean}
*/
get canNavigate() {
return this.canList || this.canCreateWorkerLed;
}
}
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default class ScopesScopeWorkersController extends Controller {
// =services
@service intl;
// =attributes
/**
* Translated workers breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.intl.t('resources.worker.title_plural');
}
}
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default class ScopesScopeWorkersNewController extends Controller {
// =services
@service intl;
// =attributes
/**
* Translated new worker breadcrumb
* @type {string}
*/
get breadCrumb() {
return this.intl.t('titles.new');
}
}
+3
View File
@@ -125,6 +125,9 @@ Router.map(function () {
);
this.route('new');
});
this.route('workers', function () {
this.route('new');
});
});
});
@@ -0,0 +1,31 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class ScopesScopeWorkersRoute extends Route {
// =services
@service can;
@service session;
@service router;
// =methods
/**
* If arriving here unauthenticated, redirect to index for further processing.
*/
beforeModel() {
if (!this.session.isAuthenticated) this.router.transitionTo('index');
}
/**
* Load all workers.
* @return {WorkerModel}
*/
model() {
const scope = this.modelFor('scopes.scope');
const { id: scope_id } = scope;
if (this.can.can('list worker', scope, { collection: 'workers' })) {
return this.store.query('worker', { scope_id });
}
}
}
@@ -0,0 +1,11 @@
import Route from '@ember/routing/route';
export default class ScopesScopeWorkersIndexRoute extends Route {
// =methods
setupController(controller) {
const scope = this.modelFor('scopes.scope');
super.setupController(...arguments);
controller.setProperties({ scope });
}
}
@@ -0,0 +1,3 @@
import Route from '@ember/routing/route';
export default class ScopesScopeWorkersNewRoute extends Route {}
+11
View File
@@ -22,6 +22,17 @@
{{t 'resources.org.title_plural'}}
</nav.link>
</Rose::Nav::Sidebar>
{{#if (can 'navigate worker' @model collection='workers')}}
<Rose::Nav::Sidebar
@title={{t 'resources.worker.title_plural'}}
as |nav|
>
<nav.link @route='scopes.scope.workers'>
<Rose::Icon @name='flight-icons/svg/cpu-16' @size='medium' />
{{t 'resources.worker.title_plural'}}
</nav.link>
</Rose::Nav::Sidebar>
{{/if}}
{{/if}}
{{#if @model.isOrg}}
@@ -0,0 +1,2 @@
{{page-title (t 'resources.worker.title_plural')}}
{{outlet}}
@@ -0,0 +1,66 @@
<Rose::Layout::Page as |page|>
<page.breadcrumbs>
<BreadCrumbs />
</page.breadcrumbs>
<page.header>
<h2>
{{t 'resources.worker.title_plural'}}
<DocLink @doc='worker' @iconSize='large' />
</h2>
</page.header>
<page.actions>
{{#if (can 'create worker led worker' this.scope collection='workers')}}
{{#if @model}}
<Rose::LinkButton @route='scopes.scope.workers.new' @style='primary'>{{t
'titles.new'
}}</Rose::LinkButton>
{{/if}}
{{/if}}
</page.actions>
<page.body>
{{#if @model}}
{{! TODO: List view }}
{{else}}
<Rose::Layout::Centered>
<Rose::Message
@title={{t 'resources.worker.messages.welcome.title'}}
as |message|
>
<message.description>
{{#if (can 'list worker' this.scope collection='workers')}}
{{! can list (at least): show default welcome message}}
{{t 'resources.worker.description'}}
{{else if (can
'create worker led worker' this.scope collection='workers'
)}}
{{! can create (only): show create-but-not-list welcome message}}
{{t
'descriptions.create-but-not-list'
resource=(t 'resources.worker.title_plural')
}}
{{else}}
{{! can neither list nor create }}
{{t
'descriptions.neither-list-nor-create'
resource=(t 'resources.worker.title_plural')
}}
{{/if}}
</message.description>
{{#if
(can 'create worker led worker' this.scope collection='workers')
}}
<message.link @route='scopes.scope.workers.new'>
<Rose::Icon @name='flight-icons/svg/plus-circle-16' />
{{t 'titles.new'}}
</message.link>
{{/if}}
</Rose::Message>
</Rose::Layout::Centered>
{{/if}}
</page.body>
</Rose::Layout::Page>
@@ -0,0 +1,2 @@
{{page-title (t 'titles.new')}}
{{outlet}}
+2
View File
@@ -97,6 +97,8 @@ module.exports = function (environment) {
'getting-started.desktop': '/getting-started/desktop',
'api-client.cli': '/api-client/cli',
'api-client.api': '/api-client/api',
worker: '/workers',
'worker.new': '/workers/new',
},
},
@@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Controller | scopes/scope/workers', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let controller = this.owner.lookup('controller:scopes/scope/workers');
assert.ok(controller);
});
});
@@ -0,0 +1,12 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Controller | scopes/scope/workers/new', function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup('controller:scopes/scope/workers/new');
assert.ok(controller);
});
});
@@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Route | scopes/scope/workers', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let route = this.owner.lookup('route:scopes/scope/workers');
assert.ok(route);
});
});
@@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Route | scopes/scope/workers/index', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let route = this.owner.lookup('route:scopes/scope/workers/index');
assert.ok(route);
});
});
@@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Route | scopes/scope/workers/new', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let route = this.owner.lookup('route:scopes/scope/workers/new');
assert.ok(route);
});
});