From 64d66c8034a2e5c5cc69fa7779f00e15ec876e18 Mon Sep 17 00:00:00 2001 From: Mathis Neumann <mathis@simpletechs.net> Date: Mon, 25 Jul 2016 14:50:21 +0200 Subject: [PATCH] reload data if revision check failed --- app/adapters/application.js | 2 +- app/models/baseentity.js | 5 ++- app/models/revision.js | 11 +++++ app/models/system.js | 4 +- app/routes/deployments/single.js | 69 +++++++++++++++++++++++++++----- app/services/changelog-stream.js | 6 +++ app/services/session.js | 8 ++-- package.json | 1 + 8 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 app/models/revision.js diff --git a/app/adapters/application.js b/app/adapters/application.js index 3e8a768..cf5ce31 100644 --- a/app/adapters/application.js +++ b/app/adapters/application.js @@ -12,7 +12,7 @@ export default BaseAdapter.extend(UrlTemplates, { urlSegments: { systemId() { - return this.get('session.systemId'); + return this.get('session.system.id'); } } }); \ No newline at end of file diff --git a/app/models/baseentity.js b/app/models/baseentity.js index bba66d4..a9403e2 100644 --- a/app/models/baseentity.js +++ b/app/models/baseentity.js @@ -4,5 +4,8 @@ import attr from 'ember-data/attr'; export default Model.extend({ // id: attr('string') - not allowed to be listed by ember systemId: attr('string'), - type: attr('string') + type: attr('string'), + revisionNumber: attr('number'), + changelogSequence: attr('number'), + lastUpdate: attr('date') }); diff --git a/app/models/revision.js b/app/models/revision.js new file mode 100644 index 0000000..30dad42 --- /dev/null +++ b/app/models/revision.js @@ -0,0 +1,11 @@ +import BaseEntity from './baseentity'; +import attr from 'ember-data/attr'; + +const Model = BaseEntity.extend({ + technology: attr('string'), + sourceId: attr('string'), + targetId: attr('string'), + workload: attr('number') +}); + +export default Model; \ No newline at end of file diff --git a/app/models/system.js b/app/models/system.js index 037318c..6beb1bd 100644 --- a/app/models/system.js +++ b/app/models/system.js @@ -1,8 +1,10 @@ import BaseEntity from './baseentity'; import attr from 'ember-data/attr'; +import { memberAction } from 'ember-api-actions'; const Model = BaseEntity.extend({ - name: attr('string') + name: attr('string'), + getRevision: memberAction({ path: 'revision', type: 'GET', urlType: 'findRecord'}) }); Model.reopenClass({ diff --git a/app/routes/deployments/single.js b/app/routes/deployments/single.js index 495b417..1c9363a 100644 --- a/app/routes/deployments/single.js +++ b/app/routes/deployments/single.js @@ -5,9 +5,7 @@ export default Ember.Route.extend({ changelogStream: Ember.inject.service(), model(params) { const systemId = params.systemId; - this.set('session.systemId', systemId); // add the system to all requests - const changelogStream = this.get('changelogStream'); // lazy loaded, requires session id - changelogStream.connect(systemId); + /* * note that findAll returns an Observable Array which automatically @@ -19,8 +17,47 @@ export default Ember.Route.extend({ * 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 */ + 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 + }); + }, + loadSystem(systemId) { + return this.store.findRecord('system', systemId) + .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; + }); + }, + loadRevision(system) { + return system.getRevision(); + }, + loadMetaModel(system) { const load = (type) => this.store.findAll(type); - return Ember.RSVP.hash({ nodes: load('node'), nodeGroups: load('nodegroup'), @@ -28,16 +65,25 @@ export default Ember.Route.extend({ serviceInstances: load('serviceinstance'), communications: load('communication'), communicationInstances: load('communicationinstance') - }).then((models) => { - this.debug('loaded models', models); - return { - systemId: systemId, - instances: models, - mode: this.get('routeName').split('.')[0] - }; }); }, + 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; + }, actions: { loadDetails(rawEntity) { this.debug('loadDetails action', rawEntity); @@ -63,6 +109,7 @@ export default Ember.Route.extend({ // 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')); } } } diff --git a/app/services/changelog-stream.js b/app/services/changelog-stream.js index 52849da..d0432b2 100644 --- a/app/services/changelog-stream.js +++ b/app/services/changelog-stream.js @@ -9,6 +9,11 @@ export default Ember.Service.extend({ this.debug('session', this.get('systemId')); }, connect(systemId) { + if(this.get('socket')) { + this.debug('already connected, disconnecting first'); + this.disconnect(); + } + this.set('shouldClose', false); this.debug('setting up websocket', systemId); @@ -40,6 +45,7 @@ export default Ember.Service.extend({ this.debug('disconnect'); this.set('shouldClose', true); this.get('socket').close(); + this.set('socket', null); // just in case it disconnected right before disconnect() was called. clearTimeout(this.get('reconnectionTimeout')); diff --git a/app/services/session.js b/app/services/session.js index 03aee72..6247afa 100644 --- a/app/services/session.js +++ b/app/services/session.js @@ -8,10 +8,10 @@ import Ember from 'ember'; * @public */ export default Ember.Service.extend({ - /** systemId store the id of the system which is currently used for + /** system stores the system which is currently used for * loading metamodel components - * @type {String} - * @property systemId + * @type {System} + * @property system */ - systemId: null + system: null }); diff --git a/package.json b/package.json index 26a5e72..246c296 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "devDependencies": { "broccoli-asset-rev": "^2.2.0", "ember-ajax": "0.7.1", + "ember-api-actions": "0.1.6", "ember-browserify": "^1.1.8", "ember-cli": "2.4.2", "ember-cli-app-version": "^1.0.0", -- GitLab