blob: eb41f382dd7fe89bad700acb5e9f35279ee890a2 [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';
9export interface IXosServiceGraphStore {
10 get(): Observable<IXosServiceGraph>;
Matteo Scandolo968e7f22017-03-03 11:49:18 -080011 getCoarse(): Observable<IXosServiceGraph>;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080012}
13
14export class XosServiceGraphStore implements IXosServiceGraphStore {
15 static $inject = [
16 '$log',
17 'XosModelStore',
Matteo Scandolobafd8d62017-03-29 23:23:00 -070018 'XosDebouncer'
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080019 ];
Matteo Scandolo968e7f22017-03-03 11:49:18 -080020
21 // graph data store
Matteo Scandolo75171782017-03-08 14:17:01 -080022 private graphData: BehaviorSubject<IXosFineGrainedGraphData> = new BehaviorSubject({
Matteo Scandolo968e7f22017-03-03 11:49:18 -080023 services: [],
Matteo Scandolo75171782017-03-08 14:17:01 -080024 tenants: [],
25 networks: [],
Matteo Scandolod4878532017-03-20 17:39:55 -070026 subscribers: [],
27 servicedependencys: []
Matteo Scandolo968e7f22017-03-03 11:49:18 -080028 });
29
Matteo Scandolo265c2042017-03-20 10:15:40 -070030 private emptyGraph: IXosServiceGraph = {
31 nodes: [],
32 links: []
33 };
34
Matteo Scandolo75171782017-03-08 14:17:01 -080035 // representation of the graph as D3 requires
Matteo Scandolo265c2042017-03-20 10:15:40 -070036 private d3CoarseGraph = new BehaviorSubject(this.emptyGraph);
37 private d3FineGrainedGraph = new BehaviorSubject(this.emptyGraph);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080038
39 // storing locally reference to the data model
40 private services;
41 private tenants;
Matteo Scandolo75171782017-03-08 14:17:01 -080042 private subscribers;
43 private networks;
Matteo Scandolod4878532017-03-20 17:39:55 -070044 private servicedependencys;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080045
46 // debounced functions
47 private handleData;
48
49 // datastore
Matteo Scandoloa160eef2017-03-06 17:21:26 -080050 private ServiceSubscription: Subscription;
51 private TenantSubscription: Subscription;
Matteo Scandolo75171782017-03-08 14:17:01 -080052 private SubscriberSubscription: Subscription;
53 private NetworkSubscription: Subscription;
Matteo Scandolod4878532017-03-20 17:39:55 -070054 private ServiceDependencySubscription: Subscription;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080055
56 constructor (
57 private $log: ng.ILogService,
58 private XosModelStore: IXosModelStoreService,
Matteo Scandolobafd8d62017-03-29 23:23:00 -070059 private XosDebouncer: IXosDebouncer
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080060 ) {
61
Matteo Scandolo968e7f22017-03-03 11:49:18 -080062 this.$log.info(`[XosServiceGraphStore] Setup`);
63
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080064 // we want to have a quiet period of 500ms from the last event before doing anything
Matteo Scandolo968e7f22017-03-03 11:49:18 -080065 this.handleData = this.XosDebouncer.debounce(this._handleData, 500, this, false);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080066
Matteo Scandolo968e7f22017-03-03 11:49:18 -080067 // observe models and populate graphData
Matteo Scandoloa160eef2017-03-06 17:21:26 -080068 this.ServiceSubscription = this.XosModelStore.query('Service', '/core/services')
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080069 .subscribe(
70 (res) => {
71 this.combineData(res, 'services');
72 },
73 (err) => {
Matteo Scandolo75171782017-03-08 14:17:01 -080074 this.$log.error(`[XosServiceGraphStore] Service Observable: `, err);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080075 }
76 );
77
Matteo Scandolod4878532017-03-20 17:39:55 -070078 this.ServiceDependencySubscription = this.XosModelStore.query('ServiceDependency', '/core/servicedependencys')
79 .subscribe(
80 (res) => {
81 this.combineData(res, 'servicedependencys');
82 },
83 (err) => {
84 this.$log.error(`[XosServiceGraphStore] Service Observable: `, err);
85 }
86 );
87
Matteo Scandoloa160eef2017-03-06 17:21:26 -080088 this.TenantSubscription = this.XosModelStore.query('Tenant', '/core/tenants')
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080089 .subscribe(
90 (res) => {
91 this.combineData(res, 'tenants');
92 },
93 (err) => {
Matteo Scandolo75171782017-03-08 14:17:01 -080094 this.$log.error(`[XosServiceGraphStore] Tenant Observable: `, err);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080095 }
96 );
Matteo Scandolo968e7f22017-03-03 11:49:18 -080097
Matteo Scandolo0e8a8422017-03-25 14:55:40 -070098 this.SubscriberSubscription = this.XosModelStore.query('Tenantroot', '/core/tenantroots')
Matteo Scandolo968e7f22017-03-03 11:49:18 -080099 .subscribe(
Matteo Scandolo75171782017-03-08 14:17:01 -0800100 (res) => {
101 this.combineData(res, 'subscribers');
102 },
103 (err) => {
104 this.$log.error(`[XosServiceGraphStore] Subscriber Observable: `, err);
105 }
106 );
107
108 this.NetworkSubscription = this.XosModelStore.query('Network', '/core/networks')
109 .subscribe(
110 (res) => {
111 this.combineData(res, 'networks');
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800112 },
113 (err) => {
114 this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
115 }
116 );
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800117
Matteo Scandolo75171782017-03-08 14:17:01 -0800118 // observe graphData and build Coarse and FineGrained graphs
119 this.graphData
120 .subscribe(
121 (res: IXosFineGrainedGraphData) => {
Matteo Scandolo98b5f5d2017-03-17 17:09:05 -0700122 this.$log.debug(`[XosServiceGraphStore] New graph data received`, res);
Matteo Scandolo75171782017-03-08 14:17:01 -0800123 this.graphDataToCoarseGraph(res);
124 this.graphDataToFineGrainedGraph(res);
125 },
126 (err) => {
127 this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
128 }
129 );
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800130 }
131
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800132 public get() {
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800133 return this.d3FineGrainedGraph.asObservable();
134 }
135
136 public getCoarse() {
137 return this.d3CoarseGraph.asObservable();
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800138 }
139
Matteo Scandolod4878532017-03-20 17:39:55 -0700140 private combineData(data: any, type: 'services'|'tenants'|'subscribers'|'networks'|'servicedependencys') {
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800141 switch (type) {
142 case 'services':
143 this.services = data;
144 break;
145 case 'tenants':
146 this.tenants = data;
147 break;
Matteo Scandolo75171782017-03-08 14:17:01 -0800148 case 'subscribers':
149 this.subscribers = data;
150 break;
151 case 'networks':
152 this.networks = data;
153 break;
Matteo Scandolod4878532017-03-20 17:39:55 -0700154 case 'servicedependencys':
155 this.servicedependencys = data;
156 break;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800157 }
158 this.handleData(this.services, this.tenants);
159 }
160
161 private _handleData(services: IXosServiceModel[], tenants: IXosTenantModel[]) {
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800162 this.graphData.next({
163 services: this.services,
Matteo Scandolo75171782017-03-08 14:17:01 -0800164 tenants: this.tenants,
165 subscribers: this.subscribers,
Matteo Scandolod4878532017-03-20 17:39:55 -0700166 networks: this.networks,
167 servicedependencys: this.servicedependencys
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800168 });
169 }
170
Matteo Scandolo75171782017-03-08 14:17:01 -0800171 private getNodeIndexById(id: number | string, nodes: IXosServiceModel[]) {
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800172 return _.findIndex(nodes, {id: id});
173 }
174
Matteo Scandolo75171782017-03-08 14:17:01 -0800175 private d3Id(type: string, id: number) {
176 return `${type.toLowerCase()}~${id}`;
177 }
178
179 private getTargetId(tenant: IXosTenantModel) {
180
181 let targetId;
182 if (tenant.subscriber_service_id) {
183 targetId = this.d3Id('service', tenant.subscriber_service_id);
184 }
185 else if (tenant.subscriber_tenant_id) {
186 targetId = this.d3Id('tenant', tenant.subscriber_tenant_id);
187 }
188 else if (tenant.subscriber_network_id) {
189 targetId = this.d3Id('network', tenant.subscriber_network_id);
190 }
191 else if (tenant.subscriber_root_id) {
Matteo Scandolo0e8a8422017-03-25 14:55:40 -0700192 // FIXME understand what's the correct model for the subscriber
193 targetId = this.d3Id('tenantroot', tenant.subscriber_root_id);
Matteo Scandolo75171782017-03-08 14:17:01 -0800194 }
195 return targetId;
196 }
197
198 private getSourceId(tenant: IXosTenantModel) {
199 return this.d3Id('service', tenant.provider_service_id);
200 }
201
202 private getNodeType(n: any) {
203 return n.class_names.split(',')[0].toLowerCase();
204 }
205
206 private getNodeLabel(n: any) {
207 if (this.getNodeType(n) === 'tenant') {
208 return n.id;
209 }
210 return n.humanReadableName ? n.humanReadableName : n.name;
211 }
212
213 private removeUnwantedFineGrainedData(data: IXosFineGrainedGraphData): IXosFineGrainedGraphData {
214 data.tenants = _.filter(data.tenants, t => t.kind !== 'coarse');
215 data.networks = _.filter(data.networks, n => {
216 const subscriber = _.findIndex(data.tenants, {subscriber_network_id: n.id});
217 return subscriber > -1;
218 });
219 return data;
220 }
221
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800222 private graphDataToCoarseGraph(data: IXosCoarseGraphData) {
Matteo Scandolo75171782017-03-08 14:17:01 -0800223
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700224 try {
Matteo Scandolod4878532017-03-20 17:39:55 -0700225 const links: IXosServiceGraphLink[] = _.chain(data.servicedependencys)
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700226 .map((t: IXosTenantModel) => {
227 return {
228 id: t.id,
229 source: this.getNodeIndexById(t.provider_service_id, data.services),
230 target: this.getNodeIndexById(t.subscriber_service_id, data.services),
231 model: t
232 };
233 })
234 .value();
235
236 const nodes: IXosServiceGraphNode[] = _.map(data.services, (s: IXosServiceModel) => {
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800237 return {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700238 id: s.id,
239 label: s.name,
240 model: s
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800241 };
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700242 });
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800243
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700244 let graph: IXosServiceGraph = {
245 nodes,
246 links
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800247 };
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800248
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700249 this.d3CoarseGraph.next(graph);
250 } catch (e) {
251 this.d3CoarseGraph.error(e);
252 }
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800253 }
254
Matteo Scandolo75171782017-03-08 14:17:01 -0800255 private graphDataToFineGrainedGraph(data: IXosFineGrainedGraphData) {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700256 try {
257 data = this.removeUnwantedFineGrainedData(data);
Matteo Scandolo75171782017-03-08 14:17:01 -0800258
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700259 let nodes = _.reduce(Object.keys(data), (list: any[], k: string) => {
260 return list.concat(data[k]);
261 }, []);
Matteo Scandolo75171782017-03-08 14:17:01 -0800262
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700263 nodes = _.chain(nodes)
264 .map(n => {
265 n.d3Id = this.d3Id(this.getNodeType(n), n.id);
266 return n;
267 })
268 .map(n => {
269 let node: IXosServiceGraphNode = {
270 id: n.d3Id,
271 label: this.getNodeLabel(n),
272 model: n,
273 type: this.getNodeType(n)
274 };
275 return node;
276 })
277 .value();
278
279 const links = _.reduce(data.tenants, (links: IXosServiceGraphLink[], tenant: IXosTenantModel) => {
280 const sourceId = this.getSourceId(tenant);
281 const targetId = this.getTargetId(tenant);
Omar Abdelkader81dc5922017-09-07 15:42:15 -0700282
283 if (angular.isDefined(targetId)) {
284 const tenantToSubscriber = {
285 id: `${tenant.d3Id}_${targetId}`,
286 source: this.getNodeIndexById(tenant.d3Id, nodes),
287 target: this.getNodeIndexById(targetId, nodes),
288 model: tenant
289 };
290
291 if (angular.isDefined(tenantToSubscriber.source) && angular.isDefined(tenantToSubscriber.target)) {
292 links.push(tenantToSubscriber);
293 }
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700294 }
295
Omar Abdelkader81dc5922017-09-07 15:42:15 -0700296 if (angular.isDefined(sourceId)) {
297 const tenantToProvider = {
298 id: `${sourceId}_${tenant.d3Id}`,
299 source: this.getNodeIndexById(sourceId, nodes),
300 target: this.getNodeIndexById(tenant.d3Id, nodes),
301 model: tenant
302 };
Matteo Scandolo75171782017-03-08 14:17:01 -0800303
Omar Abdelkader81dc5922017-09-07 15:42:15 -0700304 if (angular.isDefined(tenantToProvider.source) && angular.isDefined(tenantToProvider.target)) {
305 links.push(tenantToProvider);
306 }
307 }
Matteo Scandolo75171782017-03-08 14:17:01 -0800308
Matteo Scandolo6d3e80e2017-03-10 11:34:43 -0800309 return links;
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700310 }, []);
311
Matteo Scandolo265c2042017-03-20 10:15:40 -0700312 if (nodes.length === 0 && links.length === 0) {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700313 return;
Matteo Scandolo6d3e80e2017-03-10 11:34:43 -0800314 }
315
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700316 let graph: IXosServiceGraph = {
317 nodes,
318 links
Matteo Scandolo75171782017-03-08 14:17:01 -0800319 };
320
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700321 this.d3FineGrainedGraph.next(graph);
322 } catch (e) {
323 this.d3FineGrainedGraph.error(e);
Matteo Scandolo75171782017-03-08 14:17:01 -0800324 }
Matteo Scandolo75171782017-03-08 14:17:01 -0800325 }
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800326}