blob: a6ab41e7927ee318bed075a77df9fb35033b77ae [file] [log] [blame]
Matteo Scandolo72181592017-07-25 14:49:40 -07001import * as _ from 'lodash';
2import {Observable, BehaviorSubject, Subscription} from 'rxjs';
3import {
4 IXosServiceGraph, IXosServiceInstanceGraphData, IXosServiceGraphNode
5} from '../interfaces';
6import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
7import {IXosModelStoreService} from '../../datasources/stores/model.store';
8import {IXosServiceGraphStore} from './service-graph.store';
9
10export interface IXosServiceInstanceGraphStore {
11 get(): Observable<IXosServiceGraph>;
12}
13
14export class XosServiceInstanceGraphStore implements IXosServiceInstanceGraphStore {
15 static $inject = [
16 '$log',
17 'XosServiceGraphStore',
18 'XosModelStore',
19 'XosDebouncer'
20 ];
21
22 private CoarseGraphSubscription: Subscription;
23 private ServiceInstanceSubscription: Subscription;
24 private ServiceInstanceLinkSubscription: Subscription;
25 private NetworkSubscription: Subscription;
26
27 // debounced functions
28 private handleData;
29
30
31 // FIXME this is declared also in ServiceGraphStore
32 private emptyGraph: IXosServiceGraph = {
33 nodes: [],
34 links: []
35 };
36
37 // graph data store
38 private graphData: BehaviorSubject<IXosServiceInstanceGraphData> = new BehaviorSubject({
39 serviceGraph: this.emptyGraph,
40 serviceInstances: [],
41 serviceInstanceLinks: [],
42 networks: []
43 });
44
45 private d3ServiceInstanceGraph = new BehaviorSubject(this.emptyGraph);
46
47 private serviceGraph: IXosServiceGraph = this.emptyGraph;
48 private serviceInstances: any[] = [];
49 private serviceInstanceLinks: any[] = [];
50 private networks: any[] = [];
51
52 constructor (
53 private $log: ng.ILogService,
54 private XosServiceGraphStore: IXosServiceGraphStore,
55 private XosModelStore: IXosModelStoreService,
56 private XosDebouncer: IXosDebouncer
57 ) {
58 this.$log.info(`[XosServiceInstanceGraphStore] Setup`);
59
60 // we want to have a quiet period of 500ms from the last event before doing anything
61 this.handleData = this.XosDebouncer.debounce(this._handleData, 500, this, false);
62
63 this.CoarseGraphSubscription = this.XosServiceGraphStore.getCoarse()
64 .subscribe(
65 (graph: IXosServiceGraph) => {
66 this.combineData(graph, 'serviceGraph');
67 }
68 );
69
70 this.ServiceInstanceSubscription = this.XosModelStore.query('ServiceInstance', '/core/serviceinstances')
71 .subscribe(
72 (res) => {
73 this.combineData(res, 'serviceInstance');
74 },
75 (err) => {
76 this.$log.error(`[XosServiceInstanceGraphStore] Service Observable: `, err);
77 }
78 );
79
80 this.ServiceInstanceLinkSubscription = this.XosModelStore.query('ServiceInstanceLink', '/core/serviceinstancelinks')
81 .subscribe(
82 (res) => {
83 this.combineData(res, 'serviceInstanceLink');
84 },
85 (err) => {
86 this.$log.error(`[XosServiceInstanceGraphStore] Service Observable: `, err);
87 }
88 );
89
90 this.NetworkSubscription = this.XosModelStore.query('Network', '/core/networks')
91 .subscribe(
92 (res) => {
93 this.combineData(res, 'networks');
94 },
95 (err) => {
96 this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
97 }
98 );
99
100 // observe graphData and build ServiceInstance graph
101 this.graphData
102 .subscribe(
103 (res: IXosServiceInstanceGraphData) => {
104 this.$log.debug(`[XosServiceInstanceGraphStore] New graph data received`, res);
105
106 this.graphDataToD3(res);
107 },
108 (err) => {
109 this.$log.error(`[XosServiceInstanceGraphStore] graphData Observable: `, err);
110 }
111 );
112 }
113
114 public get(): Observable<IXosServiceGraph> {
115 return this.d3ServiceInstanceGraph;
116 }
117
118 // called by all the observables, combine the data in a globla graph observable
119 private combineData(data: any, type: 'serviceGraph' | 'serviceInstance' | 'serviceInstanceLink' | 'serviceInterface' | 'networks') {
120 switch (type) {
121 case 'serviceGraph':
122 this.serviceGraph = angular.copy(data);
123 break;
124 case 'serviceInstance':
125 this.serviceInstances = data;
126 break;
127 case 'serviceInstanceLink':
128 this.serviceInstanceLinks = data;
129 break;
130 case 'networks':
131 this.networks = data;
132 break;
133 }
134 this.handleData();
135 }
136
137 private _handleData() {
138 this.graphData.next({
139 serviceGraph: this.serviceGraph,
140 serviceInstances: this.serviceInstances,
141 serviceInstanceLinks: this.serviceInstanceLinks,
142 networks: this.networks
143 });
144 }
145
146 private getNodeType(n: any) {
147 return n.class_names.split(',')[0].toLowerCase();
148 }
149
150 private getNodeLabel(n: any) {
151 if (this.getNodeType(n) === 'serviceinstance') {
152 return n.name ? n.name : n.id;
153 }
154 return n.humanReadableName ? n.humanReadableName : n.name;
155 }
156
157 private d3Id(type: string, id: number) {
158 return `${type.toLowerCase()}~${id}`;
159 }
160
161 private toD3Node(n: any): IXosServiceGraphNode {
162 return {
163 id: this.d3Id(this.getNodeType(n), n.id),
164 label: this.getNodeLabel(n),
165 model: n,
166 type: this.getNodeType(n)
167 };
168 }
169
170 private getServiceInstanceIndexById(l: any, nodes: any[], where: 'source' | 'target'): string {
171 if (where === 'source') {
172 return _.find(nodes, {id: `serviceinstance~${l.provider_service_instance_id}`});
173 }
174 else {
175 if (l.subscriber_service_id) {
176 return _.find(nodes, {id: `service~${l.subscriber_service_id}`});
177 }
178 else if (l.subscriber_network_id) {
179 return _.find(nodes, {id: `network~${l.subscriber_network_id}`});
180 }
181 else if (l.subscriber_service_instance_id) {
182 return _.find(nodes, {id: `serviceinstance~${l.subscriber_service_instance_id}`});
183 }
184 }
185 }
186
187 private getOwnerById(id: number, nodes: any[]): any {
188 return _.find(nodes, {id: `service~${id}`});
189 }
190
191 private graphDataToD3(data: IXosServiceInstanceGraphData) {
192 try {
193 // get all the nodes
194 let nodes = _.chain(data.serviceGraph.nodes)
195 .map(n => {
196 // HACK we are receiving node as d3 models
197 return n.model;
198 })
199 .map(n => {
200 return this.toD3Node(n);
201 })
202 .value();
203
204 data.serviceInstances = _.chain(data.serviceInstances)
205 .map(n => {
206 return this.toD3Node(n);
207 })
208 .value();
209 nodes = nodes.concat(data.serviceInstances);
210
211 data.networks = _.chain(data.networks)
212 .filter(n => {
213 const subscriber = _.findIndex(data.serviceInstanceLinks, {subscriber_network_id: n.id});
214 return subscriber > -1;
215 })
216 .map(n => {
217 return this.toD3Node(n);
218 })
219 .value();
220 nodes = nodes.concat(data.networks);
221
222 let links = data.serviceGraph.links;
223
224 // create the links starting from the coarse ones
225 links = _.reduce(data.serviceInstanceLinks, (links, l) => {
226 let link = {
227 id: `service_instance_link~${l.id}`,
228 source: this.getServiceInstanceIndexById(l, nodes, 'source'),
229 target: this.getServiceInstanceIndexById(l, nodes, 'target'),
230 model: l,
231 d3Class: 'service-instance'
232 };
233 links.push(link);
234 return links;
235 }, data.serviceGraph.links);
236
237 const linksToService = _.reduce(data.serviceInstances, (links, n) => {
238 if (angular.isDefined(n.model.owner_id)) {
239 let link = {
240 id: `owner~${n.id}`,
241 source: n,
242 target: this.getOwnerById(n.model.owner_id, nodes),
243 model: n,
244 d3Class: 'owner'
245 };
246 links.push(link);
247 }
248 return links;
249 }, []);
250
251 links = links.concat(linksToService);
252
253 let graph: IXosServiceGraph = {
254 nodes,
255 links
256 };
257
258 this.d3ServiceInstanceGraph.next(graph);
259 } catch (e) {
260 this.d3ServiceInstanceGraph.error(e);
261 }
262 }
263}