blob: e3deda303c2dc067a77cbb0ea5b67f2d292033b3 [file] [log] [blame]
Matteo Scandolofb46ae62017-08-08 09:10:50 -07001
2/*
3 * Copyright 2017-present Open Networking Foundation
4
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8
9 * http://www.apache.org/licenses/LICENSE-2.0
10
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080019/// <reference path="../../../../typings/index.d.ts"/>
Matteo Scandolo86bc26a2017-01-18 11:06:47 -080020import * as _ from 'lodash';
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080021import {BehaviorSubject, Observable} from 'rxjs/Rx';
22import {IWSEvent, IWSEventService} from '../websocket/global';
23import {IXosResourceService} from '../rest/model.rest';
24import {IStoreHelpersService} from '../helpers/store.helpers';
Matteo Scandoloba0d92e2017-03-02 16:47:46 -080025import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
Matteo Scandolo63498472017-09-26 17:21:41 -070026import {IXosModeldefsCache} from '../helpers/modeldefs.service';
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080027
Matteo Scandolo47860fe2017-02-02 12:05:55 -080028export interface IXosModelStoreService {
Matteo Scandolo1aee1982017-02-17 08:33:23 -080029 query(model: string, apiUrl?: string): Observable<any>;
Matteo Scandolo5d962a32017-08-01 18:16:14 -070030 get(model: string, id: string | number): Observable<any>;
Matteo Scandolo86bc26a2017-01-18 11:06:47 -080031 search(modelName: string): any[];
Matteo Scandolo29edc0f2018-04-26 17:19:10 +020032 clean(): void;
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080033}
34
Matteo Scandolo1aee1982017-02-17 08:33:23 -080035export class XosModelStore implements IXosModelStoreService {
Matteo Scandolo63498472017-09-26 17:21:41 -070036 static $inject = [
37 '$log',
38 'WebSocket',
39 'StoreHelpers',
40 'ModelRest',
41 'XosDebouncer',
42 'XosModeldefsCache'
43 ];
Matteo Scandolob8d06372019-03-06 13:48:03 -080044
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080045 private _collections: any; // NOTE contains a map of {model: BehaviourSubject}
Matteo Scandoloba0d92e2017-03-02 16:47:46 -080046 private efficientNext: any; // NOTE debounce next
Matteo Scandolob8d06372019-03-06 13:48:03 -080047 private _ws_subscriptions: any = {}; // NOTE contains a list of models that already subscribed to the WS obeservable
48
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080049 constructor(
Matteo Scandolo17bf8242017-01-23 17:30:39 -080050 private $log: ng.ILogService,
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080051 private webSocket: IWSEventService,
52 private storeHelpers: IStoreHelpersService,
Matteo Scandolobac22452017-01-03 16:35:32 -080053 private ModelRest: IXosResourceService,
Matteo Scandolo63498472017-09-26 17:21:41 -070054 private XosDebouncer: IXosDebouncer,
55 private XosModeldefsCache: IXosModeldefsCache
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080056 ) {
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080057 this._collections = {};
Matteo Scandolo968e7f22017-03-03 11:49:18 -080058 this.efficientNext = this.XosDebouncer.debounce(this.next, 500, this, false);
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080059 }
60
Matteo Scandolo29edc0f2018-04-26 17:19:10 +020061 public clean() {
62 this._collections = {};
63 }
64
Matteo Scandolo5d962a32017-08-01 18:16:14 -070065 public query(modelName: string, apiUrl?: string): Observable<any> {
Matteo Scandolo04f487c2017-09-12 10:37:48 -070066 this.$log.debug(`[XosModelStore] QUERY: ${modelName}`);
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080067 // if there isn't already an observable for that item
Matteo Scandolo042ea632017-03-01 19:02:34 -080068 // create a new one and .next() is called by this.loadInitialData once data are received
Matteo Scandolo1aee1982017-02-17 08:33:23 -080069 if (!this._collections[modelName]) {
70 this._collections[modelName] = new BehaviorSubject([]); // NOTE maybe this can be created when we get response from the resource
71 this.loadInitialData(modelName, apiUrl);
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080072 }
73
Matteo Scandolob8d06372019-03-06 13:48:03 -080074 if (!angular.isDefined(this._ws_subscriptions[modelName])) {
75 // NOTE we need to subscribe to the WS observable only once
76 const s = this.webSocket.list()
77 .filter((e: IWSEvent) => e.model === modelName)
78 .subscribe(
79 (event: IWSEvent) => {
80 this.$log.debug(`[XosModelStore] WS Event`, event);
81 if (event.deleted) {
82 this.storeHelpers.removeItemFromCollection(event, this._collections[modelName]);
83 }
84 else {
85 this.storeHelpers.updateCollection(event, this._collections[modelName]);
86 }
87 },
88 err => this.$log.error
89 );
90 this._ws_subscriptions[modelName] = s;
91 }
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080092
Matteo Scandolo1aee1982017-02-17 08:33:23 -080093 return this._collections[modelName].asObservable();
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080094 }
95
Matteo Scandolo86bc26a2017-01-18 11:06:47 -080096 public search(modelName: string): any[] {
Matteo Scandolo1aee1982017-02-17 08:33:23 -080097 try {
98 const res = _.reduce(Object.keys(this._collections), (results, k) => {
99 let partialRes;
100 // NOTE wrapped in a try catch as some subject may be errored, due to not available REST endpoint
101 try {
102 partialRes = _.filter(this._collections[k].value, i => {
103 if (i && i.humanReadableName) {
104 return i.humanReadableName.toLowerCase().indexOf(modelName) > -1;
105 }
106 else if (i && i.name) {
107 return i.name.toLowerCase().indexOf(modelName) > -1;
108 }
109 return false;
110 });
111 } catch (e) {
112 partialRes = [];
Matteo Scandolo86bc26a2017-01-18 11:06:47 -0800113 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800114 partialRes.map(m => {
Matteo Scandolo86bc26a2017-01-18 11:06:47 -0800115 m.modelName = k;
116 return m;
117 });
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800118 return results.concat(partialRes);
119 }, []);
120 return res;
121 } catch (e) {
122 return [];
123 }
Matteo Scandolo86bc26a2017-01-18 11:06:47 -0800124 }
125
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700126 public get(modelName: string, modelId: string | number): Observable<any> {
Matteo Scandolo04f487c2017-09-12 10:37:48 -0700127 this.$log.debug(`[XosModelStore] GET: ${modelName} [${modelId}]`);
128 const subject = new BehaviorSubject({});
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700129
Matteo Scandolo04f487c2017-09-12 10:37:48 -0700130 if (angular.isString(modelId)) {
131 modelId = parseInt(modelId, 10);
132 }
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700133
Matteo Scandolo04f487c2017-09-12 10:37:48 -0700134 this.query(modelName)
Matteo Scandolob8d06372019-03-06 13:48:03 -0800135 .filter((res) => _.findIndex(res, {id: modelId}) > -1)
Matteo Scandolo04f487c2017-09-12 10:37:48 -0700136 .subscribe((res) => {
137 const model = _.find(res, {id: modelId});
138 if (model) {
139 this.$log.debug(`[XosModelStore] GET: Calling "next" on: ${modelName} [${modelId}]`);
140 subject.next(model);
141 }
142 });
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700143
144 return subject.asObservable();
Matteo Scandolo04964232017-01-07 12:53:46 -0800145 }
146
Matteo Scandoloba0d92e2017-03-02 16:47:46 -0800147 private next(subject: BehaviorSubject<any>): void {
148 subject.next(subject.value);
149 }
150
Matteo Scandolo1a85b0f2019-03-13 15:52:48 -0700151 private loadInitialData(model: string, apiUrl: string) {
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700152 // TODO provide always the apiUrl together with the query() params
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800153 if (!angular.isDefined(apiUrl)) {
Matteo Scandolo63498472017-09-26 17:21:41 -0700154 apiUrl = this.XosModeldefsCache.getApiUrlFromModel(this.XosModeldefsCache.get(model));
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800155 }
156 this.ModelRest.getResource(apiUrl).query().$promise
Matteo Scandolof2c3ed62016-12-15 14:32:50 -0800157 .then(
158 res => {
Matteo Scandolof9dd4d02016-12-22 15:17:01 -0800159 this._collections[model].next(res);
Matteo Scandolo17bf8242017-01-23 17:30:39 -0800160 })
161 .catch(
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800162 err => {
163 this._collections[model].error(err);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800164 }
Matteo Scandolof2c3ed62016-12-15 14:32:50 -0800165 );
166 }
167}