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