diff --git a/addons/api/mirage/factories/auth-method.js b/addons/api/mirage/factories/auth-method.js index 61d29726b..1d0b29df8 100644 --- a/addons/api/mirage/factories/auth-method.js +++ b/addons/api/mirage/factories/auth-method.js @@ -10,7 +10,10 @@ export default factory.extend({ withAccountsAndUsers: trait({ afterCreate(authMethod, server) { const { scope } = authMethod; - server.createList('user', 5, { scope }).map(user => { + const users = server.createList('user', 5, { scope }); + // Create authorized user: user123 + users.push(server.create('user', { scope, id: 'user123' })); + users.map(user => { const { id } = server.create('account', { scope, authMethod }); user.update({ accountIds: [id] }); }); diff --git a/addons/api/mirage/factories/session.js b/addons/api/mirage/factories/session.js index 0100a4b75..1292d3e10 100644 --- a/addons/api/mirage/factories/session.js +++ b/addons/api/mirage/factories/session.js @@ -41,6 +41,29 @@ export default factory.extend({ } } } + }), + + /** + * Sessions generated with this trait will automatically form + * associations to target, host set, and host (if available) within + * the current scope and for specified user. + */ + withUserAssociations: trait({ + afterCreate(record, server) { + const scopeId = record.scopeId; + const user = record.user; + record.update({ user }); + const target = server.schema.targets.where({ scopeId }).models[0]; + if (target) { + record.update({ target }); + const hostSet = target.hostSets.models[0]; + if (hostSet) { + record.update({ hostSet }); + const host = hostSet.hosts.models[0]; + if (host) record.update({ host }); + } + } + } }) }); diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js index cc821e6dd..9961b7010 100644 --- a/addons/api/mirage/scenarios/default.js +++ b/addons/api/mirage/scenarios/default.js @@ -29,6 +29,9 @@ export default function(server) { server.createList('host-catalog', 2, { scope }, 'withChildren'); server.createList('target', 2, { scope }, 'withRandomHostSets'); server.createList('session', 4, { scope }, 'withAssociations'); + // Create sessions for all users + server.schema.users.where({ scopeId: scope.scope.id }).models.forEach(user => + server.createList('session', 4, { scope, user }, 'withUserAssociations') + ); }); - } diff --git a/ui/desktop/app/router.js b/ui/desktop/app/router.js index 9480e547d..bcf4938e5 100644 --- a/ui/desktop/app/router.js +++ b/ui/desktop/app/router.js @@ -14,6 +14,7 @@ Router.map(function () { }); this.route('projects', function () { this.route('targets', function () {}); + this.route('sessions', function() {}); }); }); }); diff --git a/ui/desktop/app/routes/scopes/scope/projects/sessions.js b/ui/desktop/app/routes/scopes/scope/projects/sessions.js new file mode 100644 index 000000000..7430c7764 --- /dev/null +++ b/ui/desktop/app/routes/scopes/scope/projects/sessions.js @@ -0,0 +1,66 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { all, hash } from 'rsvp'; +import { A } from '@ember/array'; + +export default class ScopesScopeProjectsSessionsRoute extends Route { + + // =services + + @service session; + + // =methods + + /** + * If arriving here unauthenticated, redirect to index for further processing. + */ + beforeModel() { + if (!this.session.isAuthenticated) this.transitionTo('index'); + } + + /** + * Loads all sessions under the current scope and encapsulates them into + * an array of objects filtering to current user + * @return {Promise{[{session: SessionModel, target: TargetModel}]}} + */ + async model() { + const projects = this.modelFor('scopes.scope.projects'); + const userId = this.session.data.authenticated.user_id; + let projectSessions = await all( + projects.map(({ id: scope_id }) => + this.store.query('session', { scope_id }) + ) + ); + + // Filter sessions to current user + let sessions = projectSessions.map(sessions => { + return sessions.filter(session => { + console.log(session.user_id, userId) + return session.user_id === userId + }); + }).flat(); + + const sessionAggregates = await all( + sessions.map(session => hash({ + session, + target: session.target_id + ? ( + this.store.peekRecord('target', session.target_id) || + this.store.findRecord('target', session.target_id) + ) + : null, + })) + ); + + // Sort sessions by time created... + let sortedSessionAggregates = + A(sessionAggregates).sortBy('session.created_time').reverse(); + // Then move active sessions to the top... + sortedSessionAggregates = [ + ...sortedSessionAggregates.filter((aggregate) => aggregate.session.status === 'active'), + ...sortedSessionAggregates.filter((aggregate) => aggregate.session.status !== 'active'), + ]; + + return sortedSessionAggregates; + } +} diff --git a/ui/desktop/app/routes/scopes/scope/projects/sessions/index.js b/ui/desktop/app/routes/scopes/scope/projects/sessions/index.js new file mode 100644 index 000000000..85b153379 --- /dev/null +++ b/ui/desktop/app/routes/scopes/scope/projects/sessions/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsSessionsIndexRoute extends Route { +} diff --git a/ui/desktop/app/templates/scopes/scope.hbs b/ui/desktop/app/templates/scopes/scope.hbs index 763b790a7..6a8cd0d89 100644 --- a/ui/desktop/app/templates/scopes/scope.hbs +++ b/ui/desktop/app/templates/scopes/scope.hbs @@ -7,6 +7,13 @@ @size="medium" /> {{t "resources.target.title_plural"}} + + + + {{t "resources.session.title_plural"}} + diff --git a/ui/desktop/app/templates/scopes/scope/projects/sessions.hbs b/ui/desktop/app/templates/scopes/scope/projects/sessions.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/desktop/app/templates/scopes/scope/projects/sessions.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs b/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs new file mode 100644 index 000000000..d0847dbb3 --- /dev/null +++ b/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs @@ -0,0 +1,58 @@ + + + +

{{t "resources.session.title_plural"}}

+
+ + + + {{#if @model}} + + + + + {{t "form.id.label"}} + {{t "resources.target.title"}} + {{t "form.started.label"}} + {{t "form.status.label"}} + + + + {{#each @model as |sessionAggregate|}} + + + + {{sessionAggregate.session.id}} + + {{sessionAggregate.target.displayName}} + + + + + {{sessionAggregate.session.status}} + + + {{/each}} + + + + {{else}} + + + + + {{t "resources.session.messages.none.description"}} + + + + + {{/if}} + + + +
diff --git a/ui/desktop/tests/unit/routes/scopes/scope/projects/sessions-test.js b/ui/desktop/tests/unit/routes/scopes/scope/projects/sessions-test.js new file mode 100644 index 000000000..358be9959 --- /dev/null +++ b/ui/desktop/tests/unit/routes/scopes/scope/projects/sessions-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/sessions', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/sessions'); + assert.ok(route); + }); +}); diff --git a/ui/desktop/tests/unit/routes/scopes/scope/projects/sessions/index-test.js b/ui/desktop/tests/unit/routes/scopes/scope/projects/sessions/index-test.js new file mode 100644 index 000000000..5c63b65eb --- /dev/null +++ b/ui/desktop/tests/unit/routes/scopes/scope/projects/sessions/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/sessions/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/sessions/index'); + assert.ok(route); + }); +});