[CORD-814] Building nodes and links for coarse tenancy graph

Change-Id: I85769dc4c8d7f7714fa4b59f052d0073e7b32ec5
diff --git a/src/app/core/services/helpers/debounce.helper.spec.ts b/src/app/core/services/helpers/debounce.helper.spec.ts
index 1c47dbd..e04b11e 100644
--- a/src/app/core/services/helpers/debounce.helper.spec.ts
+++ b/src/app/core/services/helpers/debounce.helper.spec.ts
@@ -22,7 +22,7 @@
 
   it('should call a function only after it has not been called for 500ms', (done) => {
     const spy = jasmine.createSpy('fn');
-    const efficientSpy = service.debounce(spy, 500, false);
+    const efficientSpy = service.debounce(spy, 500, this, false);
     /* tslint:disable */
     efficientSpy();
     efficientSpy();
diff --git a/src/app/core/services/helpers/debounce.helper.ts b/src/app/core/services/helpers/debounce.helper.ts
index 3d4011f..0534106 100644
--- a/src/app/core/services/helpers/debounce.helper.ts
+++ b/src/app/core/services/helpers/debounce.helper.ts
@@ -1,5 +1,5 @@
 export interface IXosDebouncer {
-  debounce(func: any, wait: number, immediate?: boolean): any;
+  debounce(func: any, wait: number, context: any, immediate?: boolean): any;
 }
 
 export class XosDebouncer implements IXosDebouncer {
@@ -14,11 +14,9 @@
   // wait for 'wait' ms without actions to call the function
   // if 'immediate' call it immediatly then wait for 'wait'
   // NOTE that we cannot use $timeout service to debounce functions as it trigger infiniteDigest Exception
-  public debounce(func: any, wait: number, immediate?: boolean) {
+  public debounce(func: any, wait: number, context: any, immediate?: boolean) {
     let timeout;
-    const self = this;
     return function() {
-      const context = self;
       const args = arguments;
       const later = function() {
         timeout = null;
diff --git a/src/app/datasources/stores/model.store.ts b/src/app/datasources/stores/model.store.ts
index ab2f3d2..60d2473 100644
--- a/src/app/datasources/stores/model.store.ts
+++ b/src/app/datasources/stores/model.store.ts
@@ -23,7 +23,7 @@
     private XosDebouncer: IXosDebouncer
   ) {
     this._collections = {};
-    this.efficientNext = this.XosDebouncer.debounce(this.next, 500, false);
+    this.efficientNext = this.XosDebouncer.debounce(this.next, 500, this, false);
   }
 
   public query(modelName: string, apiUrl: string): Observable<any> {
diff --git a/src/app/service-graph/components/coarse/coarse.component.html b/src/app/service-graph/components/coarse/coarse.component.html
new file mode 100644
index 0000000..75ef18b
--- /dev/null
+++ b/src/app/service-graph/components/coarse/coarse.component.html
@@ -0,0 +1,3 @@
+<h1>Coarse Tenancy Graph</h1>
+
+<pre>{{vm.graph | json}}</pre>
\ No newline at end of file
diff --git a/src/app/service-graph/components/coarse/coarse.component.ts b/src/app/service-graph/components/coarse/coarse.component.ts
new file mode 100644
index 0000000..b64e806
--- /dev/null
+++ b/src/app/service-graph/components/coarse/coarse.component.ts
@@ -0,0 +1,25 @@
+import {IXosServiceGraphStore} from '../../services/graph.store';
+import {IXosServiceGraph} from '../../interfaces';
+class XosCoarseTenancyGraphCtrl {
+
+  static $inject = ['$log', 'XosServiceGraphStore'];
+
+  public graph: IXosServiceGraph;
+
+  constructor (
+    private $log: ng.ILogService,
+    private XosServiceGraphStore: IXosServiceGraphStore
+  ) {
+
+    this.XosServiceGraphStore.getCoarse()
+      .subscribe((res) => {
+        this.graph = res;
+      });
+  }
+}
+
+export const XosCoarseTenancyGraph: angular.IComponentOptions = {
+  template: require('./coarse.component.html'),
+  controllerAs: 'vm',
+  controller: XosCoarseTenancyGraphCtrl,
+};
diff --git a/src/app/service-graph/index.ts b/src/app/service-graph/index.ts
index 5c861fd..ab9207b 100644
--- a/src/app/service-graph/index.ts
+++ b/src/app/service-graph/index.ts
@@ -1,11 +1,13 @@
 import {xosDataSources} from '../datasources/index';
 import {XosServiceGraphStore} from './services/graph.store';
 import {xosCore} from '../core/index';
+import {XosCoarseTenancyGraph} from './components/coarse/coarse.component';
 export const xosServiceGraph = 'xosServiceGraph';
 
 angular
   .module(xosServiceGraph, [xosDataSources, xosCore])
   .service('XosServiceGraphStore', XosServiceGraphStore)
-  .run(($log: ng.ILogService, XosServiceGraphStore) => {
+  .component('xosCoarseTenancyGraph', XosCoarseTenancyGraph)
+  .run(($log: ng.ILogService) => {
     $log.info(`[${xosServiceGraph}] Module Setup`);
   });
diff --git a/src/app/service-graph/interfaces.ts b/src/app/service-graph/interfaces.ts
index b55c441..ab80c79 100644
--- a/src/app/service-graph/interfaces.ts
+++ b/src/app/service-graph/interfaces.ts
@@ -30,6 +30,11 @@
   subscribed_tenants_ids: number[];
 }
 
+export interface IXosCoarseGraphData {
+  services: IXosServiceModel[];
+  tenants: IXosTenantModel[];
+}
+
 export interface IXosServiceGraphNodeBadge {
   type: 'info'|'success'|'warning'|'danger';
   text: string;
@@ -37,18 +42,20 @@
 
 export interface IXosServiceGraphNode {
   id: number;
-  x: number;
-  y: number;
-  px: number;
-  py: number;
   label: string;
-  badge: IXosServiceGraphNodeBadge;
+  x?: number;
+  y?: number;
+  px?: number;
+  py?: number;
+  badge?: IXosServiceGraphNodeBadge;
+  model: IXosServiceModel;
 }
 
 export interface IXosServiceGraphLink {
   id: number;
   source: number;
   target: number;
+  model: IXosTenantModel;
 }
 
 export interface IXosServiceGraph {
diff --git a/src/app/service-graph/services/graph.store.ts b/src/app/service-graph/services/graph.store.ts
index e892b99..f9d4d15 100644
--- a/src/app/service-graph/services/graph.store.ts
+++ b/src/app/service-graph/services/graph.store.ts
@@ -1,9 +1,14 @@
+import * as _ from 'lodash';
 import {Observable, BehaviorSubject} from 'rxjs';
 import {IXosModelStoreService} from '../../datasources/stores/model.store';
-import {IXosServiceGraph, IXosServiceModel, IXosTenantModel} from '../interfaces';
+import {
+  IXosServiceGraph, IXosServiceModel, IXosTenantModel, IXosCoarseGraphData,
+  IXosServiceGraphNode, IXosServiceGraphLink
+} from '../interfaces';
 import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
 export interface IXosServiceGraphStore {
   get(): Observable<IXosServiceGraph>;
+  getCoarse(): Observable<IXosServiceGraph>;
 }
 
 export class XosServiceGraphStore implements IXosServiceGraphStore {
@@ -12,7 +17,16 @@
     'XosModelStore',
     'XosDebouncer'
   ];
-  private d3Graph = new BehaviorSubject({});
+
+  // graph data store
+  private graphData: BehaviorSubject<IXosCoarseGraphData> = new BehaviorSubject({
+    services: [],
+    tenants: []
+  });
+
+  // reprentations of the graph as D3 requires
+  private d3CoarseGraph = new BehaviorSubject({});
+  private d3FineGrainedGraph = new BehaviorSubject({});
 
   // storing locally reference to the data model
   private services;
@@ -31,12 +45,15 @@
     private XosDebouncer: IXosDebouncer
   ) {
 
+    this.$log.info(`[XosServiceGraphStore] Setup`);
+
     // we want to have a quiet period of 500ms from the last event before doing anything
-    this.handleData = this.XosDebouncer.debounce(this._handleData, 500, false);
+    this.handleData = this.XosDebouncer.debounce(this._handleData, 500, this, false);
 
     this.ServiceObservable = this.XosModelStore.query('Service', '/core/services');
-    this.TenantObservable = this.XosModelStore.query('Service', '/core/tenants');
+    this.TenantObservable = this.XosModelStore.query('Tenant', '/core/tenants');
 
+    // observe models and populate graphData
     this.ServiceObservable
       .subscribe(
         (res) => {
@@ -47,7 +64,7 @@
         }
       );
 
-    this.ServiceObservable
+    this.TenantObservable
       .subscribe(
         (res) => {
           this.combineData(res, 'tenants');
@@ -56,10 +73,30 @@
           this.$log.error(err);
         }
       );
+
+    // observe graphData and build Coarse or FineGrained graphs (based on who's subscribed)
+    this.graphData
+      .subscribe(
+        (res: IXosCoarseGraphData) => {
+          if (this.d3CoarseGraph.observers.length > 0) {
+            this.graphDataToCoarseGraph(res);
+          }
+          if (this.d3FineGrainedGraph.observers.length > 0) {
+            // TODO graphDataToFineGrainedGraph
+          }
+        },
+        (err) => {
+          this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
+        }
+      );
   }
 
   public get() {
-    return this.d3Graph.asObservable();
+    return this.d3FineGrainedGraph.asObservable();
+  }
+
+  public getCoarse() {
+    return this.d3CoarseGraph.asObservable();
   }
 
   private combineData(data: any, type: 'services'|'tenants') {
@@ -75,7 +112,40 @@
   }
 
   private _handleData(services: IXosServiceModel[], tenants: IXosTenantModel[]) {
-    this.$log.log(`XosServiceGraphStore`, services, tenants);
+    this.graphData.next({
+      services: this.services,
+      tenants: this.tenants
+    });
+  }
+
+  private graphDataToCoarseGraph(data: IXosCoarseGraphData) {
+
+    const links: IXosServiceGraphLink[] = _.chain(data.tenants)
+      .filter((t: IXosTenantModel) => t.kind === 'coarse')
+      .map((t: IXosTenantModel) => {
+        return {
+          id: t.id,
+          source: t.provider_service_id,
+          target: t.subscriber_service_id,
+          model: t
+        };
+      })
+      .value();
+
+    // NOTE show all services anyway or find only the node that have links pointing to it
+    const nodes: IXosServiceGraphNode[] = _.map(data.services, (s: IXosServiceModel) => {
+      return {
+        id: s.id,
+        label: s.name,
+        model: s
+      };
+    });
+
+    // TODO call next on this.d3CoarseGraph
+    this.d3CoarseGraph.next({
+      nodes: nodes,
+      links: links
+    });
   }
 
 }
diff --git a/src/app/views/dashboard/dashboard.html b/src/app/views/dashboard/dashboard.html
index db87d74..91481ad 100644
--- a/src/app/views/dashboard/dashboard.html
+++ b/src/app/views/dashboard/dashboard.html
@@ -1,4 +1,9 @@
  <!--<h1>Dashboard</h1>-->
+ <div class="row">
+     <div class="col-xs-12">
+         <xos-coarse-tenancy-graph></xos-coarse-tenancy-graph>
+     </div>
+ </div>
 <div class="row">
     <div class="col-xs-12" id="dashboard-component-container"></div>
 </div>