blob: 7908440179f06743c859fc3d9dda963436da75a7 [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 Scandolo0e8a8422017-03-25 14:55:40 -0700101 this.SubscriberSubscription = this.XosModelStore.query('Tenantroot', '/core/tenantroots')
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) {
Matteo Scandolo0e8a8422017-03-25 14:55:40 -0700195 // FIXME understand what's the correct model for the subscriber
196 targetId = this.d3Id('tenantroot', tenant.subscriber_root_id);
Matteo Scandolo75171782017-03-08 14:17:01 -0800197 }
198 return targetId;
199 }
200
201 private getSourceId(tenant: IXosTenantModel) {
202 return this.d3Id('service', tenant.provider_service_id);
203 }
204
205 private getNodeType(n: any) {
206 return n.class_names.split(',')[0].toLowerCase();
207 }
208
209 private getNodeLabel(n: any) {
210 if (this.getNodeType(n) === 'tenant') {
211 return n.id;
212 }
213 return n.humanReadableName ? n.humanReadableName : n.name;
214 }
215
216 private removeUnwantedFineGrainedData(data: IXosFineGrainedGraphData): IXosFineGrainedGraphData {
217 data.tenants = _.filter(data.tenants, t => t.kind !== 'coarse');
218 data.networks = _.filter(data.networks, n => {
219 const subscriber = _.findIndex(data.tenants, {subscriber_network_id: n.id});
220 return subscriber > -1;
221 });
222 return data;
223 }
224
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800225 private graphDataToCoarseGraph(data: IXosCoarseGraphData) {
Matteo Scandolo75171782017-03-08 14:17:01 -0800226
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700227 try {
Matteo Scandolod4878532017-03-20 17:39:55 -0700228 const links: IXosServiceGraphLink[] = _.chain(data.servicedependencys)
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700229 .map((t: IXosTenantModel) => {
230 return {
231 id: t.id,
232 source: this.getNodeIndexById(t.provider_service_id, data.services),
233 target: this.getNodeIndexById(t.subscriber_service_id, data.services),
234 model: t
235 };
236 })
237 .value();
238
239 const nodes: IXosServiceGraphNode[] = _.map(data.services, (s: IXosServiceModel) => {
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800240 return {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700241 id: s.id,
242 label: s.name,
243 model: s
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800244 };
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700245 });
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800246
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700247 let graph: IXosServiceGraph = {
248 nodes,
249 links
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800250 };
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800251
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700252 _.forEach(this.XosServiceGraphExtender.getCoarse(), (r: IXosServiceGraphReducer) => {
253 graph = r.reducer(graph);
254 });
255
256 this.d3CoarseGraph.next(graph);
257 } catch (e) {
258 this.d3CoarseGraph.error(e);
259 }
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800260 }
261
Matteo Scandolo75171782017-03-08 14:17:01 -0800262 private graphDataToFineGrainedGraph(data: IXosFineGrainedGraphData) {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700263 try {
264 data = this.removeUnwantedFineGrainedData(data);
Matteo Scandolo75171782017-03-08 14:17:01 -0800265
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700266 let nodes = _.reduce(Object.keys(data), (list: any[], k: string) => {
267 return list.concat(data[k]);
268 }, []);
Matteo Scandolo75171782017-03-08 14:17:01 -0800269
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700270 nodes = _.chain(nodes)
271 .map(n => {
272 n.d3Id = this.d3Id(this.getNodeType(n), n.id);
273 return n;
274 })
275 .map(n => {
276 let node: IXosServiceGraphNode = {
277 id: n.d3Id,
278 label: this.getNodeLabel(n),
279 model: n,
280 type: this.getNodeType(n)
281 };
282 return node;
283 })
284 .value();
285
286 const links = _.reduce(data.tenants, (links: IXosServiceGraphLink[], tenant: IXosTenantModel) => {
287 const sourceId = this.getSourceId(tenant);
288 const targetId = this.getTargetId(tenant);
289
290 if (!angular.isDefined(targetId)) {
291 // if the tenant is not pointing to anything, don't draw links
292 return links;
293 }
294
295 const tenantToProvider = {
296 id: `${sourceId}_${tenant.d3Id}`,
297 source: this.getNodeIndexById(sourceId, nodes),
298 target: this.getNodeIndexById(tenant.d3Id, nodes),
299 model: tenant
Matteo Scandolo75171782017-03-08 14:17:01 -0800300 };
Matteo Scandolo75171782017-03-08 14:17:01 -0800301
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700302 const tenantToSubscriber = {
303 id: `${tenant.d3Id}_${targetId}`,
304 source: this.getNodeIndexById(tenant.d3Id, nodes),
305 target: this.getNodeIndexById(targetId, nodes),
306 model: tenant
307 };
Matteo Scandolo75171782017-03-08 14:17:01 -0800308
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700309 links.push(tenantToProvider);
310 links.push(tenantToSubscriber);
Matteo Scandolo6d3e80e2017-03-10 11:34:43 -0800311 return links;
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700312 }, []);
313
Matteo Scandolo265c2042017-03-20 10:15:40 -0700314 if (nodes.length === 0 && links.length === 0) {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700315 return;
Matteo Scandolo6d3e80e2017-03-10 11:34:43 -0800316 }
317
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700318 let graph: IXosServiceGraph = {
319 nodes,
320 links
Matteo Scandolo75171782017-03-08 14:17:01 -0800321 };
322
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700323 _.forEach(this.XosServiceGraphExtender.getFinegrained(), (r: IXosServiceGraphReducer) => {
324 graph = r.reducer(graph);
325 });
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700326 this.d3FineGrainedGraph.next(graph);
327 } catch (e) {
328 this.d3FineGrainedGraph.error(e);
Matteo Scandolo75171782017-03-08 14:17:01 -0800329 }
Matteo Scandolo75171782017-03-08 14:17:01 -0800330 }
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800331}