blob: e3deda303c2dc067a77cbb0ea5b67f2d292033b3 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference path="../../../../typings/index.d.ts"/>
import * as _ from 'lodash';
import {BehaviorSubject, Observable} from 'rxjs/Rx';
import {IWSEvent, IWSEventService} from '../websocket/global';
import {IXosResourceService} from '../rest/model.rest';
import {IStoreHelpersService} from '../helpers/store.helpers';
import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
import {IXosModeldefsCache} from '../helpers/modeldefs.service';
export interface IXosModelStoreService {
query(model: string, apiUrl?: string): Observable<any>;
get(model: string, id: string | number): Observable<any>;
search(modelName: string): any[];
clean(): void;
}
export class XosModelStore implements IXosModelStoreService {
static $inject = [
'$log',
'WebSocket',
'StoreHelpers',
'ModelRest',
'XosDebouncer',
'XosModeldefsCache'
];
private _collections: any; // NOTE contains a map of {model: BehaviourSubject}
private efficientNext: any; // NOTE debounce next
private _ws_subscriptions: any = {}; // NOTE contains a list of models that already subscribed to the WS obeservable
constructor(
private $log: ng.ILogService,
private webSocket: IWSEventService,
private storeHelpers: IStoreHelpersService,
private ModelRest: IXosResourceService,
private XosDebouncer: IXosDebouncer,
private XosModeldefsCache: IXosModeldefsCache
) {
this._collections = {};
this.efficientNext = this.XosDebouncer.debounce(this.next, 500, this, false);
}
public clean() {
this._collections = {};
}
public query(modelName: string, apiUrl?: string): Observable<any> {
this.$log.debug(`[XosModelStore] QUERY: ${modelName}`);
// if there isn't already an observable for that item
// create a new one and .next() is called by this.loadInitialData once data are received
if (!this._collections[modelName]) {
this._collections[modelName] = new BehaviorSubject([]); // NOTE maybe this can be created when we get response from the resource
this.loadInitialData(modelName, apiUrl);
}
if (!angular.isDefined(this._ws_subscriptions[modelName])) {
// NOTE we need to subscribe to the WS observable only once
const s = this.webSocket.list()
.filter((e: IWSEvent) => e.model === modelName)
.subscribe(
(event: IWSEvent) => {
this.$log.debug(`[XosModelStore] WS Event`, event);
if (event.deleted) {
this.storeHelpers.removeItemFromCollection(event, this._collections[modelName]);
}
else {
this.storeHelpers.updateCollection(event, this._collections[modelName]);
}
},
err => this.$log.error
);
this._ws_subscriptions[modelName] = s;
}
return this._collections[modelName].asObservable();
}
public search(modelName: string): any[] {
try {
const res = _.reduce(Object.keys(this._collections), (results, k) => {
let partialRes;
// NOTE wrapped in a try catch as some subject may be errored, due to not available REST endpoint
try {
partialRes = _.filter(this._collections[k].value, i => {
if (i && i.humanReadableName) {
return i.humanReadableName.toLowerCase().indexOf(modelName) > -1;
}
else if (i && i.name) {
return i.name.toLowerCase().indexOf(modelName) > -1;
}
return false;
});
} catch (e) {
partialRes = [];
}
partialRes.map(m => {
m.modelName = k;
return m;
});
return results.concat(partialRes);
}, []);
return res;
} catch (e) {
return [];
}
}
public get(modelName: string, modelId: string | number): Observable<any> {
this.$log.debug(`[XosModelStore] GET: ${modelName} [${modelId}]`);
const subject = new BehaviorSubject({});
if (angular.isString(modelId)) {
modelId = parseInt(modelId, 10);
}
this.query(modelName)
.filter((res) => _.findIndex(res, {id: modelId}) > -1)
.subscribe((res) => {
const model = _.find(res, {id: modelId});
if (model) {
this.$log.debug(`[XosModelStore] GET: Calling "next" on: ${modelName} [${modelId}]`);
subject.next(model);
}
});
return subject.asObservable();
}
private next(subject: BehaviorSubject<any>): void {
subject.next(subject.value);
}
private loadInitialData(model: string, apiUrl: string) {
// TODO provide always the apiUrl together with the query() params
if (!angular.isDefined(apiUrl)) {
apiUrl = this.XosModeldefsCache.getApiUrlFromModel(this.XosModeldefsCache.get(model));
}
this.ModelRest.getResource(apiUrl).query().$promise
.then(
res => {
this._collections[model].next(res);
})
.catch(
err => {
this._collections[model].error(err);
}
);
}
}