@bennerinformatics/ember-fw-gc
Version:
A set of components, controllers, routes, and helpers used in all Group-Control managed FW App System applications
159 lines (147 loc) • 5.84 kB
JavaScript
import AppMeta from '../objects/app-meta';
import {A as emberA, isArray} from '@ember/array';
import EmberObject from '@ember/object';
import Service, {inject} from '@ember/service';
import {isEmpty, isNone} from '@ember/utils';
import RSVP from 'rsvp';
/**
* This service is a simple cache of app metas, as we need information from app
* metas for a few inter app compoments.
* This is done as a service instead of using ember data reduce dependencies and
* prevent model conflicts (plus meta does not conform to a JSON model).
*
* After you inject this service, you have four public methods which can be used to retrieve
* appMetas.
*
* @public
* @class AppMetaService
* @extends Ember.Service
* @module Services
*/
export default Service.extend({
ajax: inject(),
config: inject(),
/**
* Cache of app metas previously fetched.
* Should not be directly accessed, but can be watched
* @protected
* @property cache
* @type {EmberObject}
*/
cache: EmberObject.create(),
/**
* App promises that have not yet resolved
* @private
* @property _promises
* @type {EmberObject}
*/
_promises: EmberObject.create(),
/**
* Fetches the meta for an app
* @public
* @param {String} app App ID
* @return {Object} Javascript object of app metadata, {size: 0} if no meta found, or null if not fetched
*/
peekMeta(id) {
return this.get('cache').get(id);
},
/**
* Fetches the metadata for a list of apps
* @param {Array} ids Array of string app IDs
* @return {Array} Array of coorsponding app IDs
*/
peekMetas(ids) {
return emberA(ids).map((id) => this.peekMeta(id)).reject(isEmpty);
},
/**
* Internal function to store an array of apps to the cache
* @private
* @param {Array} apps Array of app meta
* @param {Array} ids Array of IDs to ensure are stored
*/
_cacheApps(apps, ids) {
// store all passed in app data
let metas = this.get('cache');
if (isArray(apps)) {
emberA(apps).forEach((app) => {
metas.set(app.appId, AppMeta.create(app));
});
}
// for all remaining IDs, store object of size 0 to say we tried to fetch but got nothing
if (isArray(ids)) {
emberA(ids).forEach((id) => {
if (isNone(metas.get(id))) {
metas.set(id, AppMeta.create({appId: id, size: 0}));
}
});
}
},
/**
* Internal function to request an app meta. Prevents making multiple requests for the same piece of data
* @private
* @param {String} id App ID to fetch
* @return {Promise} Promise that resolves to app meta
*/
_requestMeta(id) {
let promises = this.get('_promises');
let current = promises.get(id);
if (!isNone(current)) {
return current;
}
// make the actual fetch
let promise = this.get('ajax').request(this.get('config').formGCUrl('apps', 'meta', {ids: id})).then(({apps}) => {
this._cacheApps(apps, [id]);
promises.set(id, null);
// normalize empty to null for this method
let meta = this.peekMeta(id);
return isEmpty(meta) ? null : meta;
});
promises.set(id, promise);
return promise;
},
/**
* Fetches the meta from the server for the specified app
* @param {String} id App ID to fetch
* @param {Boolean} [reload=false] If true, fetches the data even if loaded. False returns cached data if present
* @return {Promise} Promise that resolves to app meta, or resolves to null if no access to the app
*/
findMeta(id, reload = false) {
// if not requested to reload, return previously found meta (if it exists)
if (!reload) {
let meta = this.peekMeta(id);
if (!isNone(meta)) {
// empty means we fetched it before, but got nothing, so give null in that case
return RSVP.resolve(isEmpty(meta) ? null : meta);
}
}
// if we are reloading or did not find the meta, fetch it
return this._requestMeta(id);
},
/**
* Fetches the meta from the server for the specified app
* @param {Array} ids App IDs to fetch
* @param {Boolean} [reload=false] If true, fetches the data even if loaded. False returns cached data if present
* @return {Promise} Promise that resolves to app meta
*/
findMetas(ids, reload = false) {
// TODO: probably a good idea to validate the IDs are valid apps, not that this method is used outside of the app switcher now
let idString;
if (reload) {
idString = ids.join(',');
} else {
// if we have no missing apps, return cached metas
let missing = emberA(ids).filter((id) => isNone(this.peekMeta(id)));
if (isEmpty(missing)) {
return RSVP.resolve(this.peekMetas(ids));
}
// otherwise fetch just the missing metas
idString = missing.join(',');
}
// fetch all missing metas and return the array of meta
// does not use _requestMeta as that function is designed for a single metadata only
return this.get('ajax').request(this.get('config').formGCUrl('apps', 'meta', {ids: idString})).then(({apps}) => {
this._cacheApps(apps, ids);
return this.peekMetas(ids);
});
}
});