UNPKG

cv-dialog-sdk

Version:

Catavolt Dialog Javascript API

535 lines (479 loc) 17.1 kB
import { ClientListener } from "../client/ClientListener"; import { StreamConsumer } from '../io/StreamConsumer'; import { StreamProducer } from '../io/StreamProducer'; import { ClientType, Dialog, DialogRedirection, Form, Login, NullRedirection, PropertyFormatter, Redirection, RedirectionUtil, Session, TypeNames, WorkbenchAction } from '../models'; import { LargeProperty } from '../models/LargeProperty'; import { ReadLargePropertyParameters } from '../models/ReadLargePropertyParameters'; import { DialogProxy } from '../proxy/DialogProxy'; import { CvLocale } from '../util/CvLocale'; import { Log } from '../util/Log'; import { ObjUtil } from '../util/ObjUtil'; import {SessionTimer} from "../util/SessionTimer"; import { CatavoltApi } from './CatavoltApi'; import { DialogApi } from './DialogApi'; import { DialogService } from './DialogService'; /** * Top-level entry point into the Catavolt API */ export class CatavoltApiImpl implements CatavoltApi { private static _singleton: CatavoltApiImpl; private static ONE_HOUR_IN_MILLIS: number = 60 * 60 * 1000; private static CHECK_SESSION_INTERVAL_MILLIS: number = 30 * 1000; // defaults private static SERVER_URL: string = 'https://dialog.hxgn-api.net'; private static SERVER_VERSION = 'v0'; public readonly DEFAULT_LOCALE: CvLocale = new CvLocale('en'); public dataLastChangedTime: Date = new Date(); private _dialogApi: DialogApi; private _session: Session; private _devicePropsDynamic: { [index: string]: () => string }; private _devicePropsStatic: { [index: string]: string }; private _locale: CvLocale = null; private _sessionTimer:SessionTimer = null; private _onSessionExpiration = null; /* ******************** Statics *********************** */ /** * Get the default session time * @returns {number} */ public static get defaultTTLInMillis(): number { return CatavoltApiImpl.ONE_HOUR_IN_MILLIS; } /** * Get the singleton instance of the CatavoltApiImpl * @returns {CatavoltApiImpl} */ static get singleton(): CatavoltApiImpl { if (!CatavoltApiImpl._singleton) { CatavoltApiImpl._singleton = new CatavoltApiImpl( CatavoltApiImpl.SERVER_URL, CatavoltApiImpl.SERVER_VERSION ); } return CatavoltApiImpl._singleton; } /** * Construct an CatavoltApiImpl * This should not be called directly, instead use the 'singleton' method * @private */ private constructor(serverUrl: string, serverVersion: string) { if (CatavoltApiImpl._singleton) { throw new Error('Singleton instance already created'); } this._devicePropsStatic = {}; this._devicePropsDynamic = {}; this._sessionTimer = new SessionTimer(()=>this.remainingSessionTime, CatavoltApiImpl.CHECK_SESSION_INTERVAL_MILLIS, ()=>{ this.logout().then(()=>{ this._onSessionExpiration && this._onSessionExpiration(); }); }); this.initDialogApi(serverUrl, serverVersion); CatavoltApiImpl._singleton = this; } /* ***************** Public Ops ******************* */ /** * Add or replace a dynamic device property (func) * @param propName * @param propFn */ public addDynamicDeviceProp(propName: string, propFn: () => string): void { this._devicePropsDynamic[propName] = propFn; } /** * Add or replace a static device property * * @param propName * @param propValue */ public addStaticDeviceProp(propName: string, propValue: string): void { this._devicePropsStatic[propName] = propValue; } /** * * @param {ClientListener} clientListener */ public addClientListener(clientListener:ClientListener):void { this._dialogApi.addClientListener(clientListener, this.locale); } /** * Get the preferred locale * @returns {CvLocale} */ get locale(): CvLocale { if (!this._locale) { const defaultLocale = this.session.tenantProperties.browserLocale; if (defaultLocale) { try { const localeJson = JSON.parse(defaultLocale); if (localeJson.language) { this._locale = new CvLocale(localeJson.language, localeJson.country); } } catch (err) { this._locale = this.DEFAULT_LOCALE; } } } if (!this._locale) { this._locale = this.DEFAULT_LOCALE; } return this._locale; } set locale(locale: CvLocale) { this._locale = locale; } /*@TODO*/ public changePasswordAndLogin( tenantId: string, clientType: ClientType, userId: string, existingPassword: string, newPassword: string ): Promise<Session | Redirection> { return Promise.reject(new Error('Not Yet Implemented')); } /** * Get the number of millis that the client will remain active between calls * to the server. * @returns {number} */ get clientTimeoutMillis(): number { const mins = this.session.tenantProperties.clientTimeoutMinutes; return mins ? Number(mins) * 60 * 1000 : CatavoltApiImpl.defaultTTLInMillis; } /** * Get the currency symbol override if defined from the server. * @returns {string} */ get currencySymbol(): string { const currencySymbol = this.session.tenantProperties.currencySymbol; return currencySymbol ? currencySymbol : null; } /** * Get the device props * @returns {{[p: string]: string}} */ get deviceProps(): { [index: string]: string } { const newProps: { [index: string]: string } = ObjUtil.addAllProps(this._devicePropsStatic, {}); for (const attr in this._devicePropsDynamic) { if (this._devicePropsDynamic.hasOwnProperty(attr)) { newProps[attr] = this._devicePropsDynamic[attr](); } } return newProps; } /** * Get the DialogApi instance * @returns {DialogApi} */ get dialogApi(): DialogApi { return this._dialogApi; } /** * Initialize a dialog service implementation for use by this CatavoltApiImpl * * @param serverVersion * @param serverUrl */ public initDialogApi(serverUrl: string, serverVersion: string = CatavoltApiImpl.SERVER_VERSION): void { this._dialogApi = new DialogService(new DialogProxy(), serverUrl, serverVersion); } /** * Check for the availability of the given featureSet * @see FeatureSet * @param featureSet * @returns {boolean} */ public isFeatureSetAvailable(featureSet: FeatureSet): boolean { try { const currentVersion = AppVersion.getAppVersion(this.session.serverVersion); const featureMinimumVersion = FeatureVersionMap[featureSet]; return featureMinimumVersion.isLessThanOrEqualTo(currentVersion); } catch (error) { Log.error('Failed to compare appVersions for feature ' + featureSet); Log.error(error); return false; } } /** * Checked logged in status * @returns {boolean} */ get isLoggedIn() { return !!this._session; } /** * Check offline status * * @param tenantId */ public isAnyUserInBriefcaseMode(tenantId: string): Promise<boolean> { return this.dialogApi.isAnyUserInBriefcaseMode(tenantId); } /** * Check offline status * * @param userInfo * @returns {Promise<boolean>} */ public isUserInBriefcaseMode(userInfo:{}): Promise<boolean> { return this.dialogApi.isUserInBriefcaseMode(userInfo); } /** * Log in and retrieve the Session * * @param tenantId * @param clientType * @param userId * @param password * * @returns {Promise<Session | Redirection>} */ public login( tenantId: string, clientType: ClientType, userId: string, password: string ): Promise<Session | Redirection> { if (this.isLoggedIn) { return this.logout().then(result => this.processLogin(tenantId, clientType, userId, password)); } else { return this.processLogin(tenantId, clientType, userId, password); } } public loginWithToken( tenantId: string, clientType: ClientType, permissionToken: string, proofKey: string ): Promise<Session | Redirection> { if (this.isLoggedIn) { return this.logout().then(result => this.processLogin(tenantId, clientType, null, null, permissionToken, proofKey) ); } else { return this.processLogin(tenantId, clientType, null, null, permissionToken, proofKey); } } /** * Logout and destroy the session * @returns {{sessionId:string}} */ public logout(): Promise<{ sessionId: string }> { if (this.isLoggedIn) { const sessionId = this.session.id; return this.dialogApi .deleteSession(this.session.tenantId, this.session.id) .then(result => { this._session = null; this._sessionTimer.stopSessionTimer(); return result; }) .catch(error => { Log.error(`Error logging out ${error}`); this._session = null; this._sessionTimer.stopSessionTimer(); return { sessionId }; }); } else { return Promise.resolve({ sessionId: null }); } } public openDialogWithId(dialogId: string): Promise<Dialog> { return this.dialogApi.getDialog(this.session.tenantId, this.session.id, dialogId).then((dialog: Dialog) => { dialog.initialize(this); if (dialog.view instanceof Form) { return dialog; } else { throw new Error(`Unexpected top-level dialog view type: ${dialog.view.type}`); } }); } public openDialog(redirection: DialogRedirection): Promise<Dialog> { return this.openDialogWithId(redirection.dialogId); } /** * Not yet implemented * @param {string} url * @returns {Promise<StreamProducer>} */ public openStream(url: string): Promise<StreamProducer> { // return this.dialogApi.streamUrl(null, null, url); throw Error('not yet implemented'); } public toDialogOrRedirection(resultPr: Promise<{}>): Promise<Dialog | Redirection> { return resultPr.then((actionResult: {}) => { if (RedirectionUtil.isDialogRedirection(actionResult)) { return this.openDialog(actionResult as DialogRedirection) as Promise<Dialog | Redirection>; } else if (RedirectionUtil.isRedirection(actionResult)) { return Promise.resolve(actionResult) as Promise<Dialog | Redirection>; } else { // @TODO - this shouldn't be a null redirection - what should it be? return Promise.resolve(actionResult as NullRedirection); } }); } public getRedirection(redirectionId: string): Promise<Redirection> { return this.dialogApi.getRedirection(this.session.tenantId, this.session.id, redirectionId); } /** * Open a {@link WorkbenchAction} * @param workbenchAction * @returns {Promise<{actionId:string} | Redirection>} */ public performWorkbenchAction(workbenchAction: WorkbenchAction): Promise<Redirection> { return this.performWorkbenchActionForId(workbenchAction.workbenchId, workbenchAction.id); } /** * Open a {@link WorkbenchWorkbenchAction} * @param workbenchId * @param workbenchActionId * @returns {Promise<{actionId:string} | Redirection>} */ public performWorkbenchActionForId(workbenchId: string, workbenchActionId: string): Promise<Redirection> { if (!this.isLoggedIn) { return Promise.reject(new Error('User is not logged in')); } return this.dialogApi.performWorkbenchAction( this.session.tenantId, this.session.id, workbenchId, workbenchActionId ); } /** * Refresh the CatavoltApiImpl * * @returns {Promise<Session>} */ public refreshSession(tenantId: string, sessionId: string): Promise<Session> { return this.dialogApi.getSession(tenantId, sessionId).then(session => { this._session = session; return session; }); } /** * Time remaining before this session is expired by the server * @returns {number} */ get remainingSessionTime(): number { return this.clientTimeoutMillis - (new Date().getTime() - this.dialogApi.lastServiceActivity.getTime()); } /** * * @param {ClientListener} clientListener */ public removeClientListener(clientListener:ClientListener): void { this._dialogApi.removeClientListener(clientListener); } /** * Get the Session * @returns {Session} */ get session(): Session { return this._session; } /** * Return whether or not the session has expired * @returns {boolean} */ get sessionHasExpired(): boolean { return this.remainingSessionTime < 0; } /** * Function that will be notified when the session expires * @param {() => void} onSessionExpiration */ set onSessionExpiration(onSessionExpiration:()=>void) { this._onSessionExpiration = onSessionExpiration; } /** * * @param {string} contentId * @param {StreamConsumer} streamConsumer * @returns {Promise<LargeProperty>} */ public streamContent(contentId: string, streamConsumer: StreamConsumer): Promise<LargeProperty> { return Dialog.loadLargeProperty((params: ReadLargePropertyParameters): Promise<LargeProperty> => { return this.dialogApi.getContent(this.session.tenantId, this.session.id, contentId, params); }, streamConsumer); } private processLogin( tenantId: string, clientType: ClientType, userId: string, password: string, permissionToken?: string, proofKey?: string ): Promise<Session | Redirection> { const login: Login = { userId, password, permissionToken, proofKey, clientType, deviceProperties: this.deviceProps, type: TypeNames.LoginTypeName }; return this.dialogApi.createSession(tenantId, login).then((result: Session | Redirection) => { if (result.type === TypeNames.SessionTypeName) { this._session = result as Session; this._sessionTimer.resetSessionTimer(); return result; } else { return result; } }); } } class AppVersion { public static getAppVersion(versionString: string): AppVersion { const [major, minor, patch] = versionString.split('.'); return new AppVersion(Number(major || 0), Number(minor || 0), Number(patch || 0)); } constructor(public major: number, public minor: number, public patch: number) {} /** * Is 'this' version less than or equal to the supplied version? * @param anotherVersion - the version to compare to 'this' version * @returns {boolean} */ public isLessThanOrEqualTo(anotherVersion: AppVersion): boolean { if (anotherVersion.major > this.major) { return true; } else if (anotherVersion.major === this.major) { if (anotherVersion.minor > this.minor) { return true; } else if (anotherVersion.minor === this.minor) { return anotherVersion.patch >= this.patch; } else { return false; } } else { return false; } } } export type FeatureSet = 'View_Support' | 'Unified_Search'; const FeatureVersionMap: { [featureSet: string]: AppVersion } = { View_Support: AppVersion.getAppVersion('1.3.447'), Unified_Search: AppVersion.getAppVersion('1.3.463') }; export const Catavolt: CatavoltApi = CatavoltApiImpl.singleton; export const propertyFormatter: PropertyFormatter = PropertyFormatter.singleton(Catavolt);