UNPKG

baqend

Version:

Baqend JavaScript SDK

229 lines (189 loc) 7.17 kB
import { EntityManager } from './EntityManager'; import * as message from './message'; import { Connector, Message, Response, } from './connector'; import { JsonMap, Lockable } from './util'; import { TokenStorage, TokenStorageFactory, Code } from './intersection'; import { Metamodel } from './metamodel'; const CONNECTED = Symbol('Connected'); export type ConnectData = { bloomFilterRefresh?: number, schema?: JsonMap, token?: string, device?: JsonMap, user?: JsonMap, bloomFilter?: JsonMap, }; export class EntityManagerFactory extends Lockable { public connection: Connector | null = null; public metamodel: Metamodel = this.createMetamodel(); public code: Code = new Code(this.metamodel, this); public tokenStorageFactory: TokenStorageFactory = TokenStorage.WEB_STORAGE || TokenStorage.GLOBAL; public tokenStorage: TokenStorage | null = null; public staleness: number | null = null; public connectData: ConnectData | null = null; private [CONNECTED]?: () => any; /** * Creates a new EntityManagerFactory connected to the given destination * @param [options] The destination to connect with, or an options object * @param [options.host] The destination to connect with * @param [options.port=80|443] The optional destination port to connect with * @param [options.secure=false] <code>true</code> To use a secure ssl encrypted connection * @param [options.basePath="/v1"] The base path of the api * @param [options.schema=null] The serialized schema as json used to initialize the metamodel * @param [options.tokenStorage] The tokenStorage which should be used by this emf * @param [options.tokenStorageFactory] The tokenStorage factory implementation which * should be used for token storage * @param [options.staleness=60] The maximum staleness of objects that are acceptable while reading cached * data */ constructor(options: { host?: string, port?: number, secure?: boolean, basePath?: string, schema?: JsonMap, tokenStorage?: TokenStorage, tokenStorageFactory?: TokenStorageFactory, staleness?: number } | string = {}) { super(); const opt = typeof options === 'string' ? { host: options } : options || {}; this.configure(opt); let isReady = true; let ready = new Promise<void>((success) => { this[CONNECTED] = success; }); if (opt.host) { this.connect(opt.host, opt.port, opt.secure, opt.basePath); } else { isReady = false; } if (!this.tokenStorage) { isReady = false; ready = ready .then(() => this.tokenStorageFactory.create(this.connection!.origin)) .then((tokenStorage) => { this.tokenStorage = tokenStorage; }); } if (opt.schema) { this.connectData = opt; this.metamodel.init(opt.schema); } else { isReady = false; ready = ready.then(() => { const msg = new message.Connect(); msg.withCredentials = true; // used for registered devices if (this.staleness === 0) { msg.noCache(); } if (this.tokenStorage?.token) { // disable client cache on the connect resource if we are authenticated msg.addQueryString('BCB'); } return this.send(msg); }).then((response: Response) => { this.connectData = response.entity as JsonMap; if (this.staleness === null) { this.staleness = this.connectData.bloomFilterRefresh || 60; } if (!this.metamodel.isInitialized) { this.metamodel.init(this.connectData.schema); } this.tokenStorage!.update(this.connectData.token as string | null); }); } if (!isReady) { this.withLock(() => ready, true); } } /** * Apply additional configurations to this EntityManagerFactory * @param options The additional configuration options * @param [options.tokenStorage] The tokenStorage which should be used by this emf * @param [options.tokenStorageFactory] The tokenStorage factory implementation which * should be used for token storage * @param [options.staleness=60] The maximum staleness of objects that are acceptable while reading cached * data, <code>0</code> to always bypass the browser cache * @return */ configure(options: { tokenStorage?: TokenStorage, tokenStorageFactory?: TokenStorageFactory, staleness?: number }): void { if (this.connection) { throw new Error('The EntityManagerFactory can only be configured before is is connected.'); } if (options.tokenStorage) { this.tokenStorage = options.tokenStorage; } if (options.tokenStorageFactory) { this.tokenStorageFactory = options.tokenStorageFactory; } if (options.staleness !== undefined) { this.staleness = options.staleness; } } /** * Connects this EntityManager to the given destination * @param hostOrApp The host or the app name to connect with * @param [secure=true] <code>true</code> To use a secure connection * @param [basePath="/v1"] The base path of the api * @return */ connect(hostOrApp: string, secure?: boolean, basePath?: string): Promise<this>; /** * Connects this EntityManager to the given destination * @param hostOrApp The host or the app name to connect with * @param [port=80|443] The port to connect to * @param [secure=false] <code>true</code> To use a secure connection * @param [basePath="/v1"] The base path of the api * @return */ connect(hostOrApp: string, port?: number, secure?: boolean, basePath?: string): Promise<this>; connect(hostOrApp: string, port?: number | boolean | undefined, secure?: string | boolean | undefined, basePath?: string | undefined): Promise<this> { if (this.connection) { throw new Error('The EntityManagerFactory is already connected.'); } if (typeof port === 'boolean') { return this.connect(hostOrApp, 0, port, secure as string); } this.connection = Connector.create(hostOrApp, port, secure as boolean, basePath); this[CONNECTED]!!(); return this.ready(); } /** * Creates a new Metamodel instance, which is not connected * @return A new Metamodel instance */ createMetamodel(): Metamodel { return new Metamodel(this); } /** * Create a new application-managed EntityManager. * * @param useSharedTokenStorage The token storage to persist the authorization token, or * <code>true</code> To use the shared token storage of the emf. * <code>false</code> To use a instance based storage. * * @return a new entityManager */ createEntityManager(useSharedTokenStorage?: boolean): EntityManager { const em = new EntityManager(this); if (this.isReady) { em.connected(useSharedTokenStorage); } else { em.withLock(() => this.ready().then(() => { em.connected(useSharedTokenStorage); }), true); } return em; } send(msg: Message): Promise<Response> { if (!msg.tokenStorage()) { msg.tokenStorage(this.tokenStorage); } return this.connection!.send(msg); } }