Newer
Older
/**
* Loads all necessary data for any visualisation types
*
* @class SingleDeploymentsRoute
* @extends {Ember.Route}
* @module routes
export default Ember.Route.extend({
session: Ember.inject.service(), // loads services/session.js
/**
* prepares data for route which will be passed to the controller
*
* @method model
* @param {Object} params, handled by Embers routing
* @return {Promise|Object} Object containing the systemId, revision, mode (deployments/architectures) and the instances
* @public
*/
return this.loadSystem(systemId)
.then(this.loadRevision.bind(this))
.then((revision) => {
return Ember.RSVP.resolve()
.then(this.loadMetaModel.bind(this))
.then(models => this.verifyRevision(revision, models))
.then((models) => {
this.debug('loaded models', models);
return {
systemId: systemId,
revision: revision,
instances: models,
mode: this.get('routeName').split('.')[0] // deployments/architectures
};
});
})
.catch(err => {
if(err === 'outdated') {
// wait a bit to avoid DDOS, TODO: exponential backoff?
this.refresh();
} else {
console.error('could not load models', err);
}
throw err; // TODO: handle errors in UI
});
},
/**
* load the current system record
* @param {String} systemId id of the system (from URL)
* @return {Promise|SystemRecord}
* @public
*/
return this.store.findRecord('system', systemId) // cached if user comes via navigation
.then((system) => {
this.debug('loaded system', system);
this.set('session.system', system); // add the system to all requests
const changelogStream = this.get('changelogStream'); // lazy loaded, requires session id
changelogStream.connect(system.id);
return system;
});
},
/**
* Loads the current revision from API (without caching)
* @param {SystemRecord}
* @return {Object} API response, containing changelogSequence, revisionNumber and the lastUpdated date
* @public
*/
loadRevision(system) {
return system.getRevision();
},
/**
* note that findAll returns an Observable Array which automatically
* update whenever new records are pushed into the store.
* The controller can observe this.
* Also note that since we changed the behavior of findAll() to use the systemId
* Ember will probably also update for other systems. These are filtered in the controller.
*
* We also load all the data, so that the transformation strategies can assume that the whole
* meta model is cached. This also allowes that the architecture view is only an alias
*
* @method loadMetaModel
* @return {Promise|Object} Promise for an object containing the model records in lists, where the keys are the pluralized model names
*/
loadMetaModel() {
const load = (type) => this.store.findAll(type);
return Ember.RSVP.hash({
nodes: load('node'),
nodeGroups: load('nodegroup'),
services: load('service'),
serviceInstances: load('serviceinstance'),
communications: load('communication'),
communicationInstances: load('communicationinstance')
Mathis Neumann
committed
},
/**
* checks whether any of the loaded instances from the API where created while the requests still in progress.
*
* @method verifyRevision
* @param {Object} revision Revision Object directly from the API (no Ember record, but plain Object)
* @param {Object} models Object with all instances of a model, keys are the pluralized identiefers
* @return {Promise|Object} promise that is rejected with 'outdated' if any instance is newer than expected, otherwise resolves models
*/
verifyRevision(revision, models) {
const outdatedRecords = Object
.keys(models)
.map(key => models[key])
.filter(instances => {
return instances.filter(record =>
record.get('revisionNumber') >= revision.revisionNumber && record.get('changelogSequence') > revision.changelogSequence
).length > 0;
});
if(outdatedRecords.length > 0) {
this.debug('records loaded from server seem to have changed during loading, refreshing data!', revision, models);
return Ember.RSVP.Promise.reject('outdated');
}
return models;
},
Mathis Neumann
committed
actions: {
loadDetails(rawEntity) {
this.debug('loadDetails action', rawEntity);
const entityType = rawEntity.type.toLowerCase();
const entityId = rawEntity.id;
/* I would love to not generate the url first, but there seem to be unknown (to me) assumptions about
* passing object parameters to transitionTo which break with the current path variables.
* Otherwise this would use transitionTo('deployments.single.details', {...})
Mathis Neumann
committed
*/
const url = this.router.generate(`${this.get('routeName')}.details`, { // use routeName to support architectures alias
Mathis Neumann
committed
systemId: this.get('session.systemId'),
entityType,
entityId
});
Mathis Neumann
committed
this.replaceWith(url);
},
backToSystem() {
this.replaceWith(this.get('routeName'));
willTransition(transition) {
this.debug('transition', transition.targetName, this.get('routeName'));
// do not disconnect if transitioning to a child route (details)
if (transition.targetName.indexOf(this.get('routeName')) !== 0) {
this.get('changelogStream').disconnect();
clearTimeout(this.get('refreshTimeout'));
Mathis Neumann
committed
}