cv-dialog-sdk
Version:
Catavolt Dialog Javascript API
164 lines (142 loc) • 8.03 kB
text/typescript
import {BlobClientResponse} from "../client/BlobClientResponse";
import {Client} from "../client/Client";
import {ClientListener} from "../client/ClientListener";
import {JsonClientResponse} from "../client/JsonClientResponse";
import {TextClientResponse} from "../client/TextClientResponse";
import {VoidClientResponse} from "../client/VoidClientResponse";
import {StreamProducer} from '../io/StreamProducer';
import {SdaDialogDelegate} from "../ppm/SdaDialogDelegate";
import {CvLocale} from "../util";
import {Log} from '../util/Log';
import {StringDictionary} from '../util/StringDictionary';
import {DialogDelegate} from "./DialogDelegate";
import {DialogProxyTools} from './DialogProxyTools';
import {DialogRequest} from "./DialogRequest";
import {ValueIterator} from "./ValueIterator";
export class DialogProxy implements Client {
private _clientListener: ClientListener;
private _dialogDelegateChain: DialogDelegate[];
private _initialized: boolean;
private _initializedPr: Promise<boolean>;
private _initializedRejectFn: (error) => void;
private _initializedResolveFn: (value) => void;
private _lastActivity: Date = new Date();
private _locale: CvLocale;
constructor() {
this._initialized = false;
this._initializedPr = new Promise((resolve, reject) => {
this._initializedResolveFn = resolve;
this._initializedRejectFn = reject;
});
this._dialogDelegateChain = [new SdaDialogDelegate()];
}
public clientListener() {
return this._clientListener;
}
// TODO: Shouldn't CvLocale either be a property of the Client or the Listener, not an extra parameter?
public addClientListener(clientListener: ClientListener, locale: CvLocale) {
this._clientListener = clientListener;
this._locale = locale;
}
public removeClientListener(clientListener: ClientListener) {
this._clientListener = null;
}
public isAnyUserInBriefcaseMode(tenantId: string): Promise<boolean> {
if(this._dialogDelegateChain && this._dialogDelegateChain.length > 0) {
return this._dialogDelegateChain[0].isAnyUserInBriefcaseMode(tenantId);
} else {
return Promise.resolve(false);
}
}
public isUserInBriefcaseMode(userInfo:{}): Promise<boolean> {
if(this._dialogDelegateChain && this._dialogDelegateChain.length > 0) {
return this._dialogDelegateChain[0].isUserInBriefcaseMode(userInfo);
} else {
return Promise.resolve(false);
}
}
get lastActivity(): Date {
return this._lastActivity;
}
public getBlob(baseUrl: string, resourcePath?: string): Promise<BlobClientResponse> {
const dialogRequest = DialogRequest.createFromGetRequest(baseUrl, resourcePath, null);
return this.processRequestAndResponse('getBlob', 'handleGetBlobResponse', [dialogRequest]);
}
public getText(baseUrl: string, resourcePath?: string): Promise<TextClientResponse> {
const dialogRequest = DialogRequest.createFromGetRequest(baseUrl, resourcePath, null);
return this.processRequestAndResponse('getText', 'handleGetTextResponse', [dialogRequest]);
}
public openStream(baseUrl: string, resourcePath?: string): Promise<StreamProducer> {
const dialogRequest = DialogRequest.createFromGetRequest(baseUrl, resourcePath, null);
return this.processRequestAndResponse('openStream', 'handleOpenStreamResponse', [dialogRequest]);
}
public postMultipart(baseUrl: string, resourcePath: string, formData: FormData): Promise<VoidClientResponse> {
const dialogRequest = DialogRequest.createFromPostMultipartRequest(baseUrl, resourcePath, formData);
return this.processRequestAndResponse('postMultipart', 'handlePostMultipartResponse', [dialogRequest]);
}
public getJson(baseUrl: string, resourcePath?: string, queryParams?: StringDictionary): Promise<JsonClientResponse> {
const dialogRequest = DialogRequest.createFromGetRequest(baseUrl, resourcePath, queryParams);
return this.processRequestAndResponse('getJson', 'handleGetJsonResponse', [dialogRequest]);
}
public postJson(baseUrl: string, resourcePath: string, jsonBody?: StringDictionary): Promise<JsonClientResponse> {
const dialogRequest = DialogRequest.createFromPostRequest(baseUrl, resourcePath, jsonBody);
return this.processRequestAndResponse('postJson', 'handlePostJsonResponse', [dialogRequest]);
}
public putJson(baseUrl: string, resourcePath: string, jsonBody?: StringDictionary): Promise<JsonClientResponse> {
const dialogRequest = DialogRequest.createFromPutRequest(baseUrl, resourcePath, jsonBody);
return this.processRequestAndResponse('putJson', 'handlePutJsonResponse', [dialogRequest]);
}
public deleteJson(baseUrl: string, resourcePath: string): Promise<JsonClientResponse> {
const dialogRequest = DialogRequest.createFromDeleteRequest(baseUrl, resourcePath);
return this.processRequestAndResponse('deleteJson', 'handleDeleteJsonResponse', [dialogRequest]);
}
private static async delegateRequest(previousPr: Promise<any>, delegateIterator: ValueIterator<DialogDelegate>, requestFn: string, args): Promise<any> {
const thisMethod = 'DialogProxy::delegateRequest';
await previousPr;
if (delegateIterator.done()) {
// Log.info(`${thisMethod} -- using common fetch client to process request: ${requestFn}`);
const fetchClient = DialogProxyTools.commonFetchClient();
const fetchClientParams = (args[0] as DialogRequest).fetchClientParams();
return fetchClient[requestFn].apply(fetchClient, fetchClientParams);
}
// Select next delegate
const nextDelegate = delegateIterator.next();
const nextPr = nextDelegate[requestFn].apply(nextDelegate, args);
if (!nextPr) {
// Next delegate chose to immediately skip this request, so advance to the next delegate
return this.delegateRequest(previousPr, delegateIterator, requestFn, args);
}
const response = await nextPr;
if (!response) {
// Next delegate chose to skip this request after a delay, so advance to the next delegate
// Log.info(`${thisMethod} -- delegate returned a falsey response, advancing to the next delegate with request: ${requestFn}`);
return this.delegateRequest(nextPr, delegateIterator, requestFn, args);
}
// Next delegate produced a response, so this is the future that will be processed
return nextPr;
}
private async prepareForActivity(): Promise<boolean> {
if (!this._initialized) {
// Log.info("DialogProxy::prepareForActivity -- waiting for all DialogDelegates to initialize");
const allDelegatesInitializing = this._dialogDelegateChain.map(d => d.initialize(this));
await Promise.all(allDelegatesInitializing);
this._initialized = true;
// Log.info("DialogProxy::prepareForActivity -- all DialogDelegates are initialized");
// Log.info("DialogProxy::prepareForActivity -- DialogProxy is initialized");
this._initializedResolveFn(this._initialized);
}
this._lastActivity = new Date();
return this._initializedPr;
}
private async processRequestAndResponse(requestFn: string, responseFn: string, args): Promise<any> {
await this.prepareForActivity();
const delegateIterator = new ValueIterator(this._dialogDelegateChain);
let responsePr = DialogProxy.delegateRequest(Promise.resolve(), delegateIterator, requestFn, args);
for (const d of this._dialogDelegateChain) {
const argsWithResponse = args.slice(0);
argsWithResponse.push(responsePr);
responsePr = d[responseFn].apply(d, argsWithResponse);
}
return responsePr;
}
}