blob: 10da16e521baaffa85c1baf66730c9f24bb8f7b6 [file] [log] [blame]
Matteo Scandolof2c3ed62016-12-15 14:32:50 -08001/// <reference path="../../../../typings/index.d.ts"/>
Matteo Scandolo86bc26a2017-01-18 11:06:47 -08002import * as _ from 'lodash';
Matteo Scandolof2c3ed62016-12-15 14:32:50 -08003import {BehaviorSubject, Observable} from 'rxjs/Rx';
4import {IWSEvent, IWSEventService} from '../websocket/global';
5import {IXosResourceService} from '../rest/model.rest';
6import {IStoreHelpersService} from '../helpers/store.helpers';
Matteo Scandoloba0d92e2017-03-02 16:47:46 -08007import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
Matteo Scandolof2c3ed62016-12-15 14:32:50 -08008
Matteo Scandolo47860fe2017-02-02 12:05:55 -08009export interface IXosModelStoreService {
Matteo Scandolo1aee1982017-02-17 08:33:23 -080010 query(model: string, apiUrl?: string): Observable<any>;
Matteo Scandolo5d962a32017-08-01 18:16:14 -070011 get(model: string, id: string | number): Observable<any>;
Matteo Scandolo86bc26a2017-01-18 11:06:47 -080012 search(modelName: string): any[];
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080013}
14
Matteo Scandolo1aee1982017-02-17 08:33:23 -080015export class XosModelStore implements IXosModelStoreService {
Matteo Scandoloba0d92e2017-03-02 16:47:46 -080016 static $inject = ['$log', 'WebSocket', 'StoreHelpers', 'ModelRest', 'XosDebouncer'];
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080017 private _collections: any; // NOTE contains a map of {model: BehaviourSubject}
Matteo Scandoloba0d92e2017-03-02 16:47:46 -080018 private efficientNext: any; // NOTE debounce next
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080019 constructor(
Matteo Scandolo17bf8242017-01-23 17:30:39 -080020 private $log: ng.ILogService,
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080021 private webSocket: IWSEventService,
22 private storeHelpers: IStoreHelpersService,
Matteo Scandolobac22452017-01-03 16:35:32 -080023 private ModelRest: IXosResourceService,
Matteo Scandoloba0d92e2017-03-02 16:47:46 -080024 private XosDebouncer: IXosDebouncer
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080025 ) {
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080026 this._collections = {};
Matteo Scandolo968e7f22017-03-03 11:49:18 -080027 this.efficientNext = this.XosDebouncer.debounce(this.next, 500, this, false);
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080028 }
29
Matteo Scandolo5d962a32017-08-01 18:16:14 -070030 public query(modelName: string, apiUrl?: string): Observable<any> {
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080031 // if there isn't already an observable for that item
Matteo Scandolo042ea632017-03-01 19:02:34 -080032 // create a new one and .next() is called by this.loadInitialData once data are received
Matteo Scandolo1aee1982017-02-17 08:33:23 -080033 if (!this._collections[modelName]) {
34 this._collections[modelName] = new BehaviorSubject([]); // NOTE maybe this can be created when we get response from the resource
35 this.loadInitialData(modelName, apiUrl);
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080036 }
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070037 // else manually trigger the next with the last know value to trigger the subscribe method of who's requesting this data
Matteo Scandolo042ea632017-03-01 19:02:34 -080038 else {
Matteo Scandoloba0d92e2017-03-02 16:47:46 -080039 this.efficientNext(this._collections[modelName]);
Matteo Scandolo042ea632017-03-01 19:02:34 -080040 }
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080041
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080042 this.webSocket.list()
Matteo Scandolo1aee1982017-02-17 08:33:23 -080043 .filter((e: IWSEvent) => e.model === modelName)
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080044 .subscribe(
45 (event: IWSEvent) => {
Matteo Scandolo1aee1982017-02-17 08:33:23 -080046 this.storeHelpers.updateCollection(event, this._collections[modelName]);
Matteo Scandoloa4a47112016-12-16 10:06:13 -080047 },
Matteo Scandolo38e94a82017-03-02 12:17:27 -080048 err => this.$log.error
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080049 );
Matteo Scandolof9dd4d02016-12-22 15:17:01 -080050
Matteo Scandolo1aee1982017-02-17 08:33:23 -080051 return this._collections[modelName].asObservable();
Matteo Scandolof2c3ed62016-12-15 14:32:50 -080052 }
53
Matteo Scandolo86bc26a2017-01-18 11:06:47 -080054 public search(modelName: string): any[] {
Matteo Scandolo1aee1982017-02-17 08:33:23 -080055 try {
56 const res = _.reduce(Object.keys(this._collections), (results, k) => {
57 let partialRes;
58 // NOTE wrapped in a try catch as some subject may be errored, due to not available REST endpoint
59 try {
60 partialRes = _.filter(this._collections[k].value, i => {
61 if (i && i.humanReadableName) {
62 return i.humanReadableName.toLowerCase().indexOf(modelName) > -1;
63 }
64 else if (i && i.name) {
65 return i.name.toLowerCase().indexOf(modelName) > -1;
66 }
67 return false;
68 });
69 } catch (e) {
70 partialRes = [];
Matteo Scandolo86bc26a2017-01-18 11:06:47 -080071 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -080072 partialRes.map(m => {
Matteo Scandolo86bc26a2017-01-18 11:06:47 -080073 m.modelName = k;
74 return m;
75 });
Matteo Scandolo1aee1982017-02-17 08:33:23 -080076 return results.concat(partialRes);
77 }, []);
78 return res;
79 } catch (e) {
80 return [];
81 }
Matteo Scandolo86bc26a2017-01-18 11:06:47 -080082 }
83
Matteo Scandolo5d962a32017-08-01 18:16:14 -070084 public get(modelName: string, modelId: string | number): Observable<any> {
85 const subject = new BehaviorSubject([]);
86
87 const _findModel = (subject) => {
88 this._collections[modelName]
89 .subscribe((res) => {
90 const model = _.find(res, {id: modelId});
91 if (model) {
92 subject.next(model);
93 }
94 });
95 };
96
97 if (!this._collections[modelName]) {
98 // cache the models in that collection
99 this.query(modelName)
100 .subscribe((res) => {
101 _findModel(subject);
102 });
103 }
104 else {
105 _findModel(subject);
106 }
107
108 return subject.asObservable();
Matteo Scandolo04964232017-01-07 12:53:46 -0800109 }
110
Matteo Scandoloba0d92e2017-03-02 16:47:46 -0800111 private next(subject: BehaviorSubject<any>): void {
112 subject.next(subject.value);
113 }
114
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800115 private loadInitialData(model: string, apiUrl?: string) {
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700116 // TODO provide always the apiUrl together with the query() params
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800117 if (!angular.isDefined(apiUrl)) {
118 // NOTE check what is the correct pattern to pluralize this
119 apiUrl = this.storeHelpers.urlFromCoreModel(model);
120 }
121 this.ModelRest.getResource(apiUrl).query().$promise
Matteo Scandolof2c3ed62016-12-15 14:32:50 -0800122 .then(
123 res => {
Matteo Scandolof9dd4d02016-12-22 15:17:01 -0800124 this._collections[model].next(res);
Matteo Scandolo17bf8242017-01-23 17:30:39 -0800125 })
126 .catch(
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800127 err => {
128 this._collections[model].error(err);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800129 }
Matteo Scandolof2c3ed62016-12-15 14:32:50 -0800130 );
131 }
132}