diff --git a/addons/api/addon/models/session.js b/addons/api/addon/models/session.js index 5ab2e3c39..dd27cd9c6 100644 --- a/addons/api/addon/models/session.js +++ b/addons/api/addon/models/session.js @@ -32,6 +32,14 @@ export default class SessionModel extends GeneratedSessionModel { : null; } + /** + * The target associated with this session (if loaded). + * @type {TargetModel} + */ + get target() { + return this.store.peekRecord('target', this.target_id); + } + // =methods /** diff --git a/addons/api/addon/models/target.js b/addons/api/addon/models/target.js index 8924ffbb4..6c5e1cfe8 100644 --- a/addons/api/addon/models/target.js +++ b/addons/api/addon/models/target.js @@ -45,6 +45,14 @@ export default class TargetModel extends GeneratedTargetModel { .filter(s => s && (s.target_id === this.id)); } + /** + * The project associated with this target (if already loaded). + * @type {ScopeModel} + */ + get project() { + return this.store.peekRecord('scope', this.scopeID); + } + /** * True if any sessions associated with this target are active or pending. * @type {boolean} diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index 5ae912f69..8f6503319 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -23,13 +23,16 @@ export default function() { // Scope resources - this.get('/scopes', ({ scopes }, { queryParams: { scope_id } }) => { + this.get('/scopes', ({ scopes }, { queryParams: { scope_id, recursive } }) => { // Default parent scope is global if (!scope_id) scope_id = 'global'; - const results = scopes.where(scope => { - return scope.scope ? scope.scope.id === scope_id : false - }); - return results; + if (recursive && scope_id === 'global') { + return scopes.all(); + } else { + return scopes.where(scope => { + return scope.scope ? scope.scope.id === scope_id : false + }); + } }); this.post('/scopes', function ({ scopes }) { // Parent scope comes through the payload via `scope_id`, but this needs @@ -287,7 +290,16 @@ export default function() { // target - this.get('/targets', function ({ targets }, { queryParams: { scope_id } }) { + this.get('/targets', function ({ targets }, { queryParams: { scope_id, recursive } }) { + if (recursive && scope_id === 'global') { + return targets.all(); + } else if (recursive) { + return targets.where(target => { + const targetModel = targets.find(target.id); + return (target.scopeId === scope_id) + || (targetModel?.scope?.scope?.id === scope_id) + }); + } return targets.where(target => target.scopeId === scope_id); }); this.post('/targets', function ({ targets }) { @@ -325,7 +337,7 @@ export default function() { // session - this.get('/sessions', function ({ sessions }, { queryParams: { scope_id } }) { + this.get('/sessions', function ({ sessions }, { queryParams: { scope_id, recursive } }) { // To simulate changes to `session.status` that may occur in the backend, // we quietly randomize the value of the field on GET. // To populate sessions for logged in user, @@ -333,7 +345,7 @@ export default function() { // But only if not in testing mode. // In tests, we need deterministic statuses. if (!Ember.testing) { - sessions.where(session => session.scopeId === scope_id) + sessions.all() .models .forEach(session => { session.update({ @@ -344,7 +356,16 @@ export default function() { }); }); } - return sessions.where(session => session.scopeId === scope_id) + if (recursive && scope_id === 'global') { + return sessions.all(); + } else if (recursive) { + return sessions.where(session => { + const sessionModel = sessions.find(session.id); + return (session.scopeId === scope_id) + || (sessionModel?.scope?.scope?.id === scope_id) + }); + } + return sessions.where(session => session.scopeId === scope_id); }); this.get('/sessions/:id', function ({ sessions }, { params: { id } }) { const session = sessions.find(id); diff --git a/ui/desktop/app/controllers/scopes/scope/projects/sessions/index.js b/ui/desktop/app/controllers/scopes/scope/projects/sessions/index.js new file mode 100644 index 000000000..dc54b63f0 --- /dev/null +++ b/ui/desktop/app/controllers/scopes/scope/projects/sessions/index.js @@ -0,0 +1,27 @@ +import Controller from '@ember/controller'; +import { computed } from '@ember/object'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeProjectsSessionsIndexController extends Controller { + + // =sessions + + @service session; + + // =attributes + + @computed('model.@each.{created_time,active}', 'session.data.authenticated.user_id') + get sorted() { + const userId = this.session.data.authenticated.user_id; + const sessions = this.model; + const sortedSessions = sessions + .filter(session => session.user_id === userId) + .sortBy('session.created_time').reverse(); + // Then move active sessions to the top... + return [ + ...sortedSessions.filter((session) => session.isCancelable), + ...sortedSessions.filter((session) => !session.isCancelable), + ]; + } + +} diff --git a/ui/desktop/app/routes/scopes/scope.js b/ui/desktop/app/routes/scopes/scope.js index 914aa2904..14e36f5e1 100644 --- a/ui/desktop/app/routes/scopes/scope.js +++ b/ui/desktop/app/routes/scopes/scope.js @@ -89,14 +89,14 @@ export default class ScopesScopeRoute extends Route { }); } - /** - * In global scope, redirect to first org on authentication. - */ - redirect(model) { - const { isAuthenticated } = this.session; - const { isGlobal } = model; - if (isAuthenticated && isGlobal && this.scopes.orgs?.length) { - this.replaceWith('scopes.scope', this.scopes.orgs.firstObject.id); - } - } + // /** + // * In global scope, redirect to first org on authentication. + // */ + // redirect(model) { + // const { isAuthenticated } = this.session; + // const { isGlobal } = model; + // if (isAuthenticated && isGlobal && this.scopes.orgs?.length) { + // this.replaceWith('scopes.scope', this.scopes.orgs.firstObject.id); + // } + // } } diff --git a/ui/desktop/app/routes/scopes/scope/projects.js b/ui/desktop/app/routes/scopes/scope/projects.js index 0d7ada97c..b1aacf256 100644 --- a/ui/desktop/app/routes/scopes/scope/projects.js +++ b/ui/desktop/app/routes/scopes/scope/projects.js @@ -20,10 +20,9 @@ export default class ScopesScopeProjectsRoute extends Route { * @return {Promise */ model() { - const currentScope = this.modelFor('scopes.scope'); - return this.store.query('scope', { - scope_id: currentScope.id, - }); + return this.store + .query('scope', { recursive: true, scope_id: 'global' }) + .filter(({ isProject }) => isProject); } } diff --git a/ui/desktop/app/routes/scopes/scope/projects/sessions.js b/ui/desktop/app/routes/scopes/scope/projects/sessions.js index 1c0f133ef..149aedb7f 100644 --- a/ui/desktop/app/routes/scopes/scope/projects/sessions.js +++ b/ui/desktop/app/routes/scopes/scope/projects/sessions.js @@ -1,8 +1,6 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; -import { all, hash } from 'rsvp'; import { task, timeout } from 'ember-concurrency'; -import { A } from '@ember/array'; import config from '../../../../config/environment'; import { action } from '@ember/object'; import { notifySuccess, notifyError } from 'core/decorators/notify'; @@ -49,43 +47,9 @@ export default class ScopesScopeProjectsSessionsRoute extends Route { * @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 => sessions.filter( - session => 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; + const { id: scope_id } = this.modelFor('scopes.scope'); + await this.store.query('target', { recursive: true, scope_id }); + return await this.store.query('session', { recursive: true, scope_id }); } /** diff --git a/ui/desktop/app/routes/scopes/scope/projects/targets.js b/ui/desktop/app/routes/scopes/scope/projects/targets.js index f02dd4f0f..32a804074 100644 --- a/ui/desktop/app/routes/scopes/scope/projects/targets.js +++ b/ui/desktop/app/routes/scopes/scope/projects/targets.js @@ -1,6 +1,5 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; -import { all } from 'rsvp'; import { action } from '@ember/object'; import { task, timeout } from 'ember-concurrency'; import { later } from '@ember/runloop'; @@ -33,7 +32,7 @@ export default class ScopesScopeProjectsTargetsRoute extends Route { @task(function * () { while(true) { yield timeout(POLL_TIMEOUT_SECONDS * 1000); - yield this.refreshSessions(); + //yield this.refreshSessions(); } }).drop() poller; @@ -51,20 +50,9 @@ export default class ScopesScopeProjectsTargetsRoute extends Route { * @return {Promise{[TargetModel]}} */ async model() { - const projects = this.modelFor('scopes.scope.projects'); - let projectTargets = await all( - projects.map(({ id: scope_id }) => - this.store.query('target', { scope_id }) - ) - ); - let targets = projectTargets.map((target) => target.toArray()).flat(); + const { id: scope_id } = this.modelFor('scopes.scope'); await this.refreshSessions(); - return targets.map((target) => { - return { - target, - project: this.store.peekRecord('scope', target.scopeID), - }; - }); + return this.store.query('target', { recursive: true, scope_id }); } /** @@ -73,13 +61,8 @@ export default class ScopesScopeProjectsTargetsRoute extends Route { * TODO: query only for sessions associated with the current user. */ async refreshSessions() { - const projects = this.modelFor('scopes.scope.projects'); - //const userId = this.session.data.authenticated.user_id; - await all( - projects.map(({ id: scope_id }) => - this.store.query('session', { scope_id }) - ) - ); + const { id: scope_id } = this.modelFor('scopes.scope'); + await this.store.query('session', { recursive: true, scope_id }); } /** @@ -115,7 +98,7 @@ export default class ScopesScopeProjectsTargetsRoute extends Route { target_id: model.id, token: this.session.data.authenticated.token }; - + if (host) options.host_id = host.id; // Create target session diff --git a/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs b/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs index 35c53bb63..3f6dccb72 100644 --- a/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs @@ -6,7 +6,7 @@ - {{#if @model}} + {{#if this.sorted}} @@ -20,22 +20,22 @@ - {{#each @model as |sessionAggregate|}} + {{#each this.sorted as |model|}} - {{sessionAggregate.session.id}} + {{model.id}} - {{sessionAggregate.target.displayName}} + {{model.target.displayName}} {{!-- Two if's here because: @@ -48,29 +48,29 @@ from other devices (or even another tab), in which case we won't have the local proxy info available here. --}} - {{#if sessionAggregate.session.isCancelable}} - {{#if sessionAggregate.session.proxy}} + {{#if model.isCancelable}} + {{#if model.proxy}} - {{sessionAggregate.session.proxy}} + {{model.proxy}} {{/if}} {{/if}} - - {{sessionAggregate.session.status}} + {{model.status}} - {{#if sessionAggregate.session.isCancelable}} - + {{#if model.isCancelable}} + {{t "actions.cancel"}} {{/if}} diff --git a/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs b/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs index b816ced05..61f61178c 100644 --- a/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs @@ -22,32 +22,32 @@ - {{model.target.displayName}} + {{model.displayName}} -

{{model.target.description}}

+

{{model.description}}

- {{model.target.id}} + {{model.id}} - {{#if model.target.type}} + {{#if model.type}} - {{t (concat "resources.target.types." model.target.type)}} + {{t (concat "resources.target.types." model.type)}} {{/if}} @@ -66,7 +66,7 @@
- + {{t "resources.session.actions.connect"}} diff --git a/ui/desktop/tests/unit/controllers/scopes/scope/projects/sessions/index-test.js b/ui/desktop/tests/unit/controllers/scopes/scope/projects/sessions/index-test.js new file mode 100644 index 000000000..1a9f45f78 --- /dev/null +++ b/ui/desktop/tests/unit/controllers/scopes/scope/projects/sessions/index-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/sessions/index', function(hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/sessions/index'); + assert.ok(controller); + }); +});