[CORD-1250] Rendering new ServiceInstance Models
Change-Id: Ic8fdb4775b119816b4b7aa085e6af699eaa13a67
diff --git a/src/app/service-graph/services/service-instance.graph.store.ts b/src/app/service-graph/services/service-instance.graph.store.ts
new file mode 100644
index 0000000..a6ab41e
--- /dev/null
+++ b/src/app/service-graph/services/service-instance.graph.store.ts
@@ -0,0 +1,263 @@
+import * as _ from 'lodash';
+import {Observable, BehaviorSubject, Subscription} from 'rxjs';
+import {
+ IXosServiceGraph, IXosServiceInstanceGraphData, IXosServiceGraphNode
+} from '../interfaces';
+import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
+import {IXosModelStoreService} from '../../datasources/stores/model.store';
+import {IXosServiceGraphStore} from './service-graph.store';
+
+export interface IXosServiceInstanceGraphStore {
+ get(): Observable<IXosServiceGraph>;
+}
+
+export class XosServiceInstanceGraphStore implements IXosServiceInstanceGraphStore {
+ static $inject = [
+ '$log',
+ 'XosServiceGraphStore',
+ 'XosModelStore',
+ 'XosDebouncer'
+ ];
+
+ private CoarseGraphSubscription: Subscription;
+ private ServiceInstanceSubscription: Subscription;
+ private ServiceInstanceLinkSubscription: Subscription;
+ private NetworkSubscription: Subscription;
+
+ // debounced functions
+ private handleData;
+
+
+ // FIXME this is declared also in ServiceGraphStore
+ private emptyGraph: IXosServiceGraph = {
+ nodes: [],
+ links: []
+ };
+
+ // graph data store
+ private graphData: BehaviorSubject<IXosServiceInstanceGraphData> = new BehaviorSubject({
+ serviceGraph: this.emptyGraph,
+ serviceInstances: [],
+ serviceInstanceLinks: [],
+ networks: []
+ });
+
+ private d3ServiceInstanceGraph = new BehaviorSubject(this.emptyGraph);
+
+ private serviceGraph: IXosServiceGraph = this.emptyGraph;
+ private serviceInstances: any[] = [];
+ private serviceInstanceLinks: any[] = [];
+ private networks: any[] = [];
+
+ constructor (
+ private $log: ng.ILogService,
+ private XosServiceGraphStore: IXosServiceGraphStore,
+ private XosModelStore: IXosModelStoreService,
+ private XosDebouncer: IXosDebouncer
+ ) {
+ this.$log.info(`[XosServiceInstanceGraphStore] 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, this, false);
+
+ this.CoarseGraphSubscription = this.XosServiceGraphStore.getCoarse()
+ .subscribe(
+ (graph: IXosServiceGraph) => {
+ this.combineData(graph, 'serviceGraph');
+ }
+ );
+
+ this.ServiceInstanceSubscription = this.XosModelStore.query('ServiceInstance', '/core/serviceinstances')
+ .subscribe(
+ (res) => {
+ this.combineData(res, 'serviceInstance');
+ },
+ (err) => {
+ this.$log.error(`[XosServiceInstanceGraphStore] Service Observable: `, err);
+ }
+ );
+
+ this.ServiceInstanceLinkSubscription = this.XosModelStore.query('ServiceInstanceLink', '/core/serviceinstancelinks')
+ .subscribe(
+ (res) => {
+ this.combineData(res, 'serviceInstanceLink');
+ },
+ (err) => {
+ this.$log.error(`[XosServiceInstanceGraphStore] Service Observable: `, err);
+ }
+ );
+
+ this.NetworkSubscription = this.XosModelStore.query('Network', '/core/networks')
+ .subscribe(
+ (res) => {
+ this.combineData(res, 'networks');
+ },
+ (err) => {
+ this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
+ }
+ );
+
+ // observe graphData and build ServiceInstance graph
+ this.graphData
+ .subscribe(
+ (res: IXosServiceInstanceGraphData) => {
+ this.$log.debug(`[XosServiceInstanceGraphStore] New graph data received`, res);
+
+ this.graphDataToD3(res);
+ },
+ (err) => {
+ this.$log.error(`[XosServiceInstanceGraphStore] graphData Observable: `, err);
+ }
+ );
+ }
+
+ public get(): Observable<IXosServiceGraph> {
+ return this.d3ServiceInstanceGraph;
+ }
+
+ // called by all the observables, combine the data in a globla graph observable
+ private combineData(data: any, type: 'serviceGraph' | 'serviceInstance' | 'serviceInstanceLink' | 'serviceInterface' | 'networks') {
+ switch (type) {
+ case 'serviceGraph':
+ this.serviceGraph = angular.copy(data);
+ break;
+ case 'serviceInstance':
+ this.serviceInstances = data;
+ break;
+ case 'serviceInstanceLink':
+ this.serviceInstanceLinks = data;
+ break;
+ case 'networks':
+ this.networks = data;
+ break;
+ }
+ this.handleData();
+ }
+
+ private _handleData() {
+ this.graphData.next({
+ serviceGraph: this.serviceGraph,
+ serviceInstances: this.serviceInstances,
+ serviceInstanceLinks: this.serviceInstanceLinks,
+ networks: this.networks
+ });
+ }
+
+ private getNodeType(n: any) {
+ return n.class_names.split(',')[0].toLowerCase();
+ }
+
+ private getNodeLabel(n: any) {
+ if (this.getNodeType(n) === 'serviceinstance') {
+ return n.name ? n.name : n.id;
+ }
+ return n.humanReadableName ? n.humanReadableName : n.name;
+ }
+
+ private d3Id(type: string, id: number) {
+ return `${type.toLowerCase()}~${id}`;
+ }
+
+ private toD3Node(n: any): IXosServiceGraphNode {
+ return {
+ id: this.d3Id(this.getNodeType(n), n.id),
+ label: this.getNodeLabel(n),
+ model: n,
+ type: this.getNodeType(n)
+ };
+ }
+
+ private getServiceInstanceIndexById(l: any, nodes: any[], where: 'source' | 'target'): string {
+ if (where === 'source') {
+ return _.find(nodes, {id: `serviceinstance~${l.provider_service_instance_id}`});
+ }
+ else {
+ if (l.subscriber_service_id) {
+ return _.find(nodes, {id: `service~${l.subscriber_service_id}`});
+ }
+ else if (l.subscriber_network_id) {
+ return _.find(nodes, {id: `network~${l.subscriber_network_id}`});
+ }
+ else if (l.subscriber_service_instance_id) {
+ return _.find(nodes, {id: `serviceinstance~${l.subscriber_service_instance_id}`});
+ }
+ }
+ }
+
+ private getOwnerById(id: number, nodes: any[]): any {
+ return _.find(nodes, {id: `service~${id}`});
+ }
+
+ private graphDataToD3(data: IXosServiceInstanceGraphData) {
+ try {
+ // get all the nodes
+ let nodes = _.chain(data.serviceGraph.nodes)
+ .map(n => {
+ // HACK we are receiving node as d3 models
+ return n.model;
+ })
+ .map(n => {
+ return this.toD3Node(n);
+ })
+ .value();
+
+ data.serviceInstances = _.chain(data.serviceInstances)
+ .map(n => {
+ return this.toD3Node(n);
+ })
+ .value();
+ nodes = nodes.concat(data.serviceInstances);
+
+ data.networks = _.chain(data.networks)
+ .filter(n => {
+ const subscriber = _.findIndex(data.serviceInstanceLinks, {subscriber_network_id: n.id});
+ return subscriber > -1;
+ })
+ .map(n => {
+ return this.toD3Node(n);
+ })
+ .value();
+ nodes = nodes.concat(data.networks);
+
+ let links = data.serviceGraph.links;
+
+ // create the links starting from the coarse ones
+ links = _.reduce(data.serviceInstanceLinks, (links, l) => {
+ let link = {
+ id: `service_instance_link~${l.id}`,
+ source: this.getServiceInstanceIndexById(l, nodes, 'source'),
+ target: this.getServiceInstanceIndexById(l, nodes, 'target'),
+ model: l,
+ d3Class: 'service-instance'
+ };
+ links.push(link);
+ return links;
+ }, data.serviceGraph.links);
+
+ const linksToService = _.reduce(data.serviceInstances, (links, n) => {
+ if (angular.isDefined(n.model.owner_id)) {
+ let link = {
+ id: `owner~${n.id}`,
+ source: n,
+ target: this.getOwnerById(n.model.owner_id, nodes),
+ model: n,
+ d3Class: 'owner'
+ };
+ links.push(link);
+ }
+ return links;
+ }, []);
+
+ links = links.concat(linksToService);
+
+ let graph: IXosServiceGraph = {
+ nodes,
+ links
+ };
+
+ this.d3ServiceInstanceGraph.next(graph);
+ } catch (e) {
+ this.d3ServiceInstanceGraph.error(e);
+ }
+ }
+}