152 lines
3.9 KiB
JavaScript
152 lines
3.9 KiB
JavaScript
import Service from '@ember/service';
|
|
import { tracked } from '@glimmer/tracking';
|
|
import { computed } from '@ember/object';
|
|
import { inject as service } from '@ember/service';
|
|
|
|
/**
|
|
* This module includes two parts:
|
|
*
|
|
* - `Scope` class: a data structure representing an org/project tuple
|
|
* - `ScopeService`: manages the scope and exposes it to the application
|
|
*/
|
|
|
|
/**
|
|
* Represents a _scope_, which is defined as an organization/project tuple.
|
|
* This class is intended as a simple data structure and is used by the
|
|
* scope service (see below).
|
|
* Immutable.
|
|
*/
|
|
class Scope {
|
|
/**
|
|
* @type {?object}
|
|
* @private
|
|
*/
|
|
#org = null;
|
|
|
|
/**
|
|
* @type {?object}
|
|
* @private
|
|
*/
|
|
#project = null;
|
|
|
|
/**
|
|
* Initializes an instance of `Scope` with optional org and/or project.
|
|
* @param {?object} org
|
|
* @param {?object} project
|
|
*/
|
|
constructor(org = null, project = null) {
|
|
if (org) this.#org = org;
|
|
if (project) this.#project = project;
|
|
}
|
|
|
|
/**
|
|
* Org for this scope. Immutable.
|
|
* @type {?object}
|
|
*/
|
|
get org() {
|
|
return this.#org;
|
|
}
|
|
|
|
/**
|
|
* Project for this scope. If org is null, so is project.
|
|
* @type {?object}
|
|
*/
|
|
get project() {
|
|
return this.org ? this.#project : null;
|
|
}
|
|
|
|
/**
|
|
* Returns the JSON serialization of this scope. This is necessary because
|
|
* scopes may be persisted in storage with their JSON representation.
|
|
* @return {?object}
|
|
*/
|
|
toJSON() {
|
|
const { org, project } = this;
|
|
return { org, project };
|
|
}
|
|
}
|
|
|
|
export { Scope };
|
|
|
|
/**
|
|
* The scope service manages and persists the current scope. To interact with
|
|
* this service, get and set `service.org` or `service.project` directly, rather
|
|
* than using the tracked `scope` member, which is internal.
|
|
*
|
|
* A scope is valid only if the user has access to the organization and project
|
|
* specified in the scope, and they exist. However, validating against
|
|
* available organizations and projects is outside the scope (tee hee) of this
|
|
* service because it is used by the adapter mixin to generate API paths, which
|
|
* could lead to problems were this service to attempt to fetch data.
|
|
*
|
|
* For now, should an invalid scope arise, it is left to the user to refresh,
|
|
* since they are likely to encounter 404s. This is an acceptable tradeoff for
|
|
* an initial release because invalid scopes are considered uncommon.
|
|
*/
|
|
export default class ScopeService extends Service {
|
|
/**
|
|
* The storage service is used to persist the current scope.
|
|
* @type {StorageService}
|
|
*/
|
|
@service storage;
|
|
|
|
/**
|
|
* The currently selected scope. This is an internal member only.
|
|
* @type {?Scope}
|
|
*/
|
|
@tracked scope = new Scope();
|
|
|
|
/**
|
|
* When setting org, `service.org = {...}`, a new scope is instantiated,
|
|
* since scopes are immutable. Also persists scope on set.
|
|
* @type {?object}
|
|
*/
|
|
@computed('scope.org')
|
|
get org() {
|
|
return this.scope ? this.scope.org : null;
|
|
}
|
|
set org(org) {
|
|
this.scope = new Scope(org, this.project);
|
|
this.saveScope(this.scope);
|
|
}
|
|
|
|
/**
|
|
* When setting project, `service.project = {...}`, a new scope is
|
|
* instantiated, since scopes are immutable. Also persists scope on set.
|
|
* @type {?object}
|
|
*/
|
|
@computed('scope.project')
|
|
get project() {
|
|
return this.scope ? this.scope.project : null;
|
|
}
|
|
set project(project) {
|
|
this.scope = new Scope(this.org, project);
|
|
this.saveScope(this.scope);
|
|
}
|
|
|
|
/**
|
|
* Saves the current scope into storage.
|
|
*/
|
|
saveScope(scope) {
|
|
this.storage.setItem('scope', scope);
|
|
}
|
|
|
|
/**
|
|
* Retrieves a saved scope from storage, if any.
|
|
* @return {?Scope}
|
|
*/
|
|
fetchScope() {
|
|
const json = this.storage.getItem('scope');
|
|
if (json) return new Scope(json.org, json.project);
|
|
}
|
|
|
|
/**
|
|
* Initializes the service's current scope from storage, if any.
|
|
*/
|
|
init() {
|
|
const savedScope = this.fetchScope();
|
|
if (savedScope) this.scope = savedScope;
|
|
super.init(...arguments);
|
|
}
|
|
}
|