blob: 08ec52bd5f04a9eb2bf4350d09bd65c5a8f9aab4 [file] [log] [blame]
Matteo Scandolo968e7f22017-03-03 11:49:18 -08001import * as _ from 'lodash';
Matteo Scandoloa160eef2017-03-06 17:21:26 -08002import {Observable, BehaviorSubject, Subscription} from 'rxjs';
Matteo Scandoloa62adbc2017-03-02 15:37:34 -08003import {IXosModelStoreService} from '../../datasources/stores/model.store';
Matteo Scandolo968e7f22017-03-03 11:49:18 -08004import {
5 IXosServiceGraph, IXosServiceModel, IXosTenantModel, IXosCoarseGraphData,
Matteo Scandolo75171782017-03-08 14:17:01 -08006 IXosServiceGraphNode, IXosServiceGraphLink, IXosFineGrainedGraphData
Matteo Scandolo968e7f22017-03-03 11:49:18 -08007} from '../interfaces';
Matteo Scandoloa62adbc2017-03-02 15:37:34 -08008import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
Matteo Scandolo7629cc42017-03-13 14:12:15 -07009import {IXosServiceGraphExtender, IXosServiceGraphReducer} from './graph.extender';
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080010export interface IXosServiceGraphStore {
11 get(): Observable<IXosServiceGraph>;
Matteo Scandolo968e7f22017-03-03 11:49:18 -080012 getCoarse(): Observable<IXosServiceGraph>;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080013}
14
15export class XosServiceGraphStore implements IXosServiceGraphStore {
16 static $inject = [
17 '$log',
18 'XosModelStore',
Matteo Scandolo7629cc42017-03-13 14:12:15 -070019 'XosDebouncer',
20 'XosServiceGraphExtender'
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080021 ];
Matteo Scandolo968e7f22017-03-03 11:49:18 -080022
23 // graph data store
Matteo Scandolo75171782017-03-08 14:17:01 -080024 private graphData: BehaviorSubject<IXosFineGrainedGraphData> = new BehaviorSubject({
Matteo Scandolo968e7f22017-03-03 11:49:18 -080025 services: [],
Matteo Scandolo75171782017-03-08 14:17:01 -080026 tenants: [],
27 networks: [],
Matteo Scandolod4878532017-03-20 17:39:55 -070028 subscribers: [],
29 servicedependencys: []
Matteo Scandolo968e7f22017-03-03 11:49:18 -080030 });
31
Matteo Scandolo265c2042017-03-20 10:15:40 -070032 private emptyGraph: IXosServiceGraph = {
33 nodes: [],
34 links: []
35 };
36
Matteo Scandolo75171782017-03-08 14:17:01 -080037 // representation of the graph as D3 requires
Matteo Scandolo265c2042017-03-20 10:15:40 -070038 private d3CoarseGraph = new BehaviorSubject(this.emptyGraph);
39 private d3FineGrainedGraph = new BehaviorSubject(this.emptyGraph);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080040
41 // storing locally reference to the data model
42 private services;
43 private tenants;
Matteo Scandolo75171782017-03-08 14:17:01 -080044 private subscribers;
45 private networks;
Matteo Scandolod4878532017-03-20 17:39:55 -070046 private servicedependencys;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080047
48 // debounced functions
49 private handleData;
50
51 // datastore
Matteo Scandoloa160eef2017-03-06 17:21:26 -080052 private ServiceSubscription: Subscription;
53 private TenantSubscription: Subscription;
Matteo Scandolo75171782017-03-08 14:17:01 -080054 private SubscriberSubscription: Subscription;
55 private NetworkSubscription: Subscription;
Matteo Scandolod4878532017-03-20 17:39:55 -070056 private ServiceDependencySubscription: Subscription;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080057
58 constructor (
59 private $log: ng.ILogService,
60 private XosModelStore: IXosModelStoreService,
Matteo Scandolo7629cc42017-03-13 14:12:15 -070061 private XosDebouncer: IXosDebouncer,
62 private XosServiceGraphExtender: IXosServiceGraphExtender
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080063 ) {
64
Matteo Scandolo968e7f22017-03-03 11:49:18 -080065 this.$log.info(`[XosServiceGraphStore] Setup`);
66
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080067 // we want to have a quiet period of 500ms from the last event before doing anything
Matteo Scandolo968e7f22017-03-03 11:49:18 -080068 this.handleData = this.XosDebouncer.debounce(this._handleData, 500, this, false);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080069
Matteo Scandolo968e7f22017-03-03 11:49:18 -080070 // observe models and populate graphData
Matteo Scandoloa160eef2017-03-06 17:21:26 -080071 this.ServiceSubscription = this.XosModelStore.query('Service', '/core/services')
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080072 .subscribe(
73 (res) => {
74 this.combineData(res, 'services');
75 },
76 (err) => {
Matteo Scandolo75171782017-03-08 14:17:01 -080077 this.$log.error(`[XosServiceGraphStore] Service Observable: `, err);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080078 }
79 );
80
Matteo Scandolod4878532017-03-20 17:39:55 -070081 this.ServiceDependencySubscription = this.XosModelStore.query('ServiceDependency', '/core/servicedependencys')
82 .subscribe(
83 (res) => {
84 this.combineData(res, 'servicedependencys');
85 },
86 (err) => {
87 this.$log.error(`[XosServiceGraphStore] Service Observable: `, err);
88 }
89 );
90
Matteo Scandoloa160eef2017-03-06 17:21:26 -080091 this.TenantSubscription = this.XosModelStore.query('Tenant', '/core/tenants')
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080092 .subscribe(
93 (res) => {
94 this.combineData(res, 'tenants');
95 },
96 (err) => {
Matteo Scandolo75171782017-03-08 14:17:01 -080097 this.$log.error(`[XosServiceGraphStore] Tenant Observable: `, err);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080098 }
99 );
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800100
Matteo Scandolo75171782017-03-08 14:17:01 -0800101 this.SubscriberSubscription = this.XosModelStore.query('Subscriber', '/core/subscribers')
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800102 .subscribe(
Matteo Scandolo75171782017-03-08 14:17:01 -0800103 (res) => {
104 this.combineData(res, 'subscribers');
105 },
106 (err) => {
107 this.$log.error(`[XosServiceGraphStore] Subscriber Observable: `, err);
108 }
109 );
110
111 this.NetworkSubscription = this.XosModelStore.query('Network', '/core/networks')
112 .subscribe(
113 (res) => {
114 this.combineData(res, 'networks');
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800115 },
116 (err) => {
117 this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
118 }
119 );
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800120
Matteo Scandolo75171782017-03-08 14:17:01 -0800121 // observe graphData and build Coarse and FineGrained graphs
122 this.graphData
123 .subscribe(
124 (res: IXosFineGrainedGraphData) => {
Matteo Scandolo98b5f5d2017-03-17 17:09:05 -0700125 this.$log.debug(`[XosServiceGraphStore] New graph data received`, res);
Matteo Scandolo75171782017-03-08 14:17:01 -0800126 this.graphDataToCoarseGraph(res);
127 this.graphDataToFineGrainedGraph(res);
128 },
129 (err) => {
130 this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
131 }
132 );
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800133 }
134
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800135 public get() {
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800136 return this.d3FineGrainedGraph.asObservable();
137 }
138
139 public getCoarse() {
140 return this.d3CoarseGraph.asObservable();
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800141 }
142
Matteo Scandolod4878532017-03-20 17:39:55 -0700143 private combineData(data: any, type: 'services'|'tenants'|'subscribers'|'networks'|'servicedependencys') {
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800144 switch (type) {
145 case 'services':
146 this.services = data;
147 break;
148 case 'tenants':
149 this.tenants = data;
150 break;
Matteo Scandolo75171782017-03-08 14:17:01 -0800151 case 'subscribers':
152 this.subscribers = data;
153 break;
154 case 'networks':
155 this.networks = data;
156 break;
Matteo Scandolod4878532017-03-20 17:39:55 -0700157 case 'servicedependencys':
158 this.servicedependencys = data;
159 break;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800160 }
161 this.handleData(this.services, this.tenants);
162 }
163
164 private _handleData(services: IXosServiceModel[], tenants: IXosTenantModel[]) {
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800165 this.graphData.next({
166 services: this.services,
Matteo Scandolo75171782017-03-08 14:17:01 -0800167 tenants: this.tenants,
168 subscribers: this.subscribers,
Matteo Scandolod4878532017-03-20 17:39:55 -0700169 networks: this.networks,
170 servicedependencys: this.servicedependencys
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800171 });
172 }
173
Matteo Scandolo75171782017-03-08 14:17:01 -0800174 private getNodeIndexById(id: number | string, nodes: IXosServiceModel[]) {
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800175 return _.findIndex(nodes, {id: id});
176 }
177
Matteo Scandolo75171782017-03-08 14:17:01 -0800178 private d3Id(type: string, id: number) {
179 return `${type.toLowerCase()}~${id}`;
180 }
181
182 private getTargetId(tenant: IXosTenantModel) {
183
184 let targetId;
185 if (tenant.subscriber_service_id) {
186 targetId = this.d3Id('service', tenant.subscriber_service_id);
187 }
188 else if (tenant.subscriber_tenant_id) {
189 targetId = this.d3Id('tenant', tenant.subscriber_tenant_id);
190 }
191 else if (tenant.subscriber_network_id) {
192 targetId = this.d3Id('network', tenant.subscriber_network_id);
193 }
194 else if (tenant.subscriber_root_id) {
195 targetId = this.d3Id('subscriber', tenant.subscriber_root_id);
196 }
197 return targetId;
198 }
199
200 private getSourceId(tenant: IXosTenantModel) {
201 return this.d3Id('service', tenant.provider_service_id);
202 }
203
204 private getNodeType(n: any) {
205 return n.class_names.split(',')[0].toLowerCase();
206 }
207
208 private getNodeLabel(n: any) {
209 if (this.getNodeType(n) === 'tenant') {
210 return n.id;
211 }
212 return n.humanReadableName ? n.humanReadableName : n.name;
213 }
214
215 private removeUnwantedFineGrainedData(data: IXosFineGrainedGraphData): IXosFineGrainedGraphData {
216 data.tenants = _.filter(data.tenants, t => t.kind !== 'coarse');
217 data.networks = _.filter(data.networks, n => {
218 const subscriber = _.findIndex(data.tenants, {subscriber_network_id: n.id});
219 return subscriber > -1;
220 });
221 return data;
222 }
223
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800224 private graphDataToCoarseGraph(data: IXosCoarseGraphData) {
Matteo Scandolo75171782017-03-08 14:17:01 -0800225
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700226 try {
Matteo Scandolod4878532017-03-20 17:39:55 -0700227 const links: IXosServiceGraphLink[] = _.chain(data.servicedependencys)
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700228 .map((t: IXosTenantModel) => {
229 return {
230 id: t.id,
231 source: this.getNodeIndexById(t.provider_service_id, data.services),
232 target: this.getNodeIndexById(t.subscriber_service_id, data.services),
233 model: t
234 };
235 })
236 .value();
237
238 const nodes: IXosServiceGraphNode[] = _.map(data.services, (s: IXosServiceModel) => {
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800239 return {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700240 id: s.id,
241 label: s.name,
242 model: s
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800243 };
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700244 });
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800245
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700246 let graph: IXosServiceGraph = {
247 nodes,
248 links
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800249 };
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800250
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700251 _.forEach(this.XosServiceGraphExtender.getCoarse(), (r: IXosServiceGraphReducer) => {
252 graph = r.reducer(graph);
253 });
254
255 this.d3CoarseGraph.next(graph);
256 } catch (e) {
257 this.d3CoarseGraph.error(e);
258 }
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800259 }
260
Matteo Scandolo75171782017-03-08 14:17:01 -0800261 private graphDataToFineGrainedGraph(data: IXosFineGrainedGraphData) {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700262 try {
263 data = this.removeUnwantedFineGrainedData(data);
Matteo Scandolo75171782017-03-08 14:17:01 -0800264
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700265 let nodes = _.reduce(Object.keys(data), (list: any[], k: string) => {
266 return list.concat(data[k]);
267 }, []);
Matteo Scandolo75171782017-03-08 14:17:01 -0800268
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700269 nodes = _.chain(nodes)
270 .map(n => {
271 n.d3Id = this.d3Id(this.getNodeType(n), n.id);
272 return n;
273 })
274 .map(n => {
275 let node: IXosServiceGraphNode = {
276 id: n.d3Id,
277 label: this.getNodeLabel(n),
278 model: n,
279 type: this.getNodeType(n)
280 };
281 return node;
282 })
283 .value();
284
285 const links = _.reduce(data.tenants, (links: IXosServiceGraphLink[], tenant: IXosTenantModel) => {
286 const sourceId = this.getSourceId(tenant);
287 const targetId = this.getTargetId(tenant);
288
289 if (!angular.isDefined(targetId)) {
290 // if the tenant is not pointing to anything, don't draw links
291 return links;
292 }
293
294 const tenantToProvider = {
295 id: `${sourceId}_${tenant.d3Id}`,
296 source: this.getNodeIndexById(sourceId, nodes),
297 target: this.getNodeIndexById(tenant.d3Id, nodes),
298 model: tenant
Matteo Scandolo75171782017-03-08 14:17:01 -0800299 };
Matteo Scandolo75171782017-03-08 14:17:01 -0800300
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700301 const tenantToSubscriber = {
302 id: `${tenant.d3Id}_${targetId}`,
303 source: this.getNodeIndexById(tenant.d3Id, nodes),
304 target: this.getNodeIndexById(targetId, nodes),
305 model: tenant
306 };
Matteo Scandolo75171782017-03-08 14:17:01 -0800307
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700308 links.push(tenantToProvider);
309 links.push(tenantToSubscriber);
Matteo Scandolo6d3e80e2017-03-10 11:34:43 -0800310 return links;
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700311 }, []);
312
Matteo Scandolo265c2042017-03-20 10:15:40 -0700313 if (nodes.length === 0 && links.length === 0) {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700314 return;
Matteo Scandolo6d3e80e2017-03-10 11:34:43 -0800315 }
316
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700317 let graph: IXosServiceGraph = {
318 nodes,
319 links
Matteo Scandolo75171782017-03-08 14:17:01 -0800320 };
321
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700322 _.forEach(this.XosServiceGraphExtender.getFinegrained(), (r: IXosServiceGraphReducer) => {
323 graph = r.reducer(graph);
324 });
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700325 this.d3FineGrainedGraph.next(graph);
326 } catch (e) {
327 this.d3FineGrainedGraph.error(e);
Matteo Scandolo75171782017-03-08 14:17:01 -0800328 }
Matteo Scandolo75171782017-03-08 14:17:01 -0800329 }
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800330}