cv-dialog-sdk
Version:
Catavolt Dialog Javascript API
401 lines (400 loc) • 14.3 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Base64 } from '../util/Base64';
import { Attachment } from './Attachment';
import { ErrorMessage } from './ErrorMessage';
import { PropertyFormatter } from './PropertyFormatter';
import { DialogModeEnum, TypeNames, ViewModeEnum } from './types';
/**
* Top-level class, representing a Catavolt 'Dialog' definition.
* All Dialogs have a composite {@link View} definition along with a single record
* or a list of records. See {@Record}
*/
export class Dialog {
constructor() {
this.children = [];
// private/protected
this._lastRefreshTime = new Date(0);
}
// protected _parentDialog;
/* public methods */
static isSearchDialog(dialog) {
return dialog.dialogClassName && dialog.dialogClassName.indexOf(Dialog.SEARCH_DIALOG_CLASS) > -1 &&
dialog.view && dialog.view.type === TypeNames.DetailsTypeName;
}
get catavolt() {
return this._catavolt;
}
get anyChildNeedsRefresh() {
return (this.children &&
this.children.some((dialog) => {
return dialog.isRefreshNeeded;
}));
}
destroy() {
return this.catavolt.dialogApi.deleteDialog(this.tenantId, this.sessionId, this.id).then(() => {
this.dialogMode = DialogModeEnum.DESTROYED;
});
}
/**
* Return the error associated with this dialog, if any
* @returns {}
*/
get error() {
if (this.hasError) {
return this.view.exception;
}
else {
return null;
}
}
/**
* Find a menu def on this dialog with the given actionId
* @param actionId
* @returns {Menu}
*/
findMenuAt(actionId) {
return this.view.findMenuAt(actionId);
}
/**
* Get a string representation of this property suitable for 'reading'
*
* @param {Property} prop
* @param {string} propName
* @returns {string}
*/
formatForRead(prop, propName) {
return PropertyFormatter.singleton(this._catavolt).formatForRead(prop, this.propDefAtName(propName));
}
/**
* Get a string representation of this property suitable for 'writing'
*
* @param {Property} prop
* @param {string} propName
* @returns {string}
*/
formatForWrite(prop, propName) {
return PropertyFormatter.singleton(this.catavolt).formatForWrite(prop, this.propDefAtName(propName));
}
/**
* Returns whether or not this dialog loaded properly
* @returns {boolean}
*/
get hasError() {
return this.view instanceof ErrorMessage;
}
/**
* Returns whether or not this Form is destroyed
* @returns {boolean}
*/
get isDestroyed() {
return this.dialogMode === DialogModeEnum.DESTROYED || this.isAnyChildDestroyed;
}
/**
* Returns whether or not the data in this dialog is out of date
* @returns {boolean}
*/
get isRefreshNeeded() {
return this._lastRefreshTime.getTime() < this.catavolt.dataLastChangedTime.getTime();
}
get isReadViewMode() {
return this.viewMode === ViewModeEnum.READ;
}
/**
* Get the last time this dialog's data was refreshed
* @returns {Date}
*/
get lastRefreshTime() {
return this._lastRefreshTime;
}
/**
* @param time
*/
set lastRefreshTime(time) {
this._lastRefreshTime = time;
}
/**
* Get the all {@link Menu}'s associated with this dialog
* @returns {Array<Menu>}
*/
get menu() {
return this.view.menu;
}
openViewWithId(viewId) {
return this.catavolt.dialogApi
.changeView(this.tenantId, this.sessionId, this.id, viewId)
.then((dialog) => {
// any new dialog needs to be initialized with the Catavolt object
dialog.initialize(this.catavolt);
this.updateSettingsWithNewDialogProperties(dialog.referringObject);
return dialog;
});
}
openView(targetViewDescriptor) {
return this.openViewWithId(targetViewDescriptor.id);
}
/**
* Get the title of this dialog
* @returns {string}
*/
get paneTitle() {
let title = this.view.findTitle();
if (!title) {
title = this.description;
}
return title;
}
/**
* Parses a value to prepare for 'writing' back to the server
* @param formattedValue
* @param propName
* @returns {}
*/
parseValue(formattedValue, propName) {
return PropertyFormatter.singleton(this._catavolt).parse(formattedValue, this.propDefAtName(propName));
}
/**
* Get the property definition for a property name
* @param propName
* @returns {PropertyDef}
*/
propDefAtName(propName) {
return this.recordDef.propDefAtName(propName);
}
/**
* Read all the large property values into memory in this {@link Record}
*
* @param {string} recordId
* @returns {Promise<LargeProperty[]>}
*/
readLargeProperties(recordId) {
return Promise.all(this.recordDef.propertyDefs
.filter((propDef) => {
return propDef.isLargePropertyType;
})
.map((propDef) => {
return this.readLargeProperty(propDef.propertyName, recordId);
}));
}
/**
* Read a large property into memory
*
* @param {string} propertyName
* @param {string} recordId
* @returns {Promise<LargeProperty>}
*/
readLargeProperty(propertyName, recordId) {
return this.loadLargeProperty(propertyName, recordId);
}
/**
* Stream the encoded chunks of a large property without retaining them
* The streamConsumer will receive Base64 encoded chunks with callbacks. hasMore will
* be false with the final chunk.
* @param {StreamConsumer} streamConsumer
* @param {string} propertyName
* @param {string} recordId
* @returns {Promise<LargeProperty>}
*/
streamLargeProperty(streamConsumer, propertyName, recordId) {
return this.loadLargeProperty(propertyName, recordId, streamConsumer);
}
/*
get parentDialog():Dialog {
return this._parentDialog;
}
*/
/**
* Get the all {@link ViewDescriptor}'s associated with this Form
* @returns {Array<ViewDescriptor>}
*/
get viewDescs() {
return this.availableViews;
}
initialize(catavolt) {
this._catavolt = catavolt;
if (this.children) {
this.children.forEach((child) => {
// @TODO add this if needed
// child._parentDialog = this;
child.initialize(catavolt);
});
}
}
invokeMenuActionWithId(actionId, actionParams) {
return this.catavolt.dialogApi
.performAction(this.tenantId, this.sessionId, this.id, actionId, actionParams)
.then((result) => {
// @TODO - update relevant referring dialog settings on 'this' dialog
this.updateSettingsWithNewDialogProperties(result.referringObject);
if (result.refreshNeeded) {
this.catavolt.dataLastChangedTime = new Date();
}
return result;
});
}
/**
* Perform this action associated with the given Menu on this dialog.
* The targets array is expected to be an array of object ids.
* @param {Menu} menu
* @param {ActionParameters} actionParams
* @returns {Promise<{actionId: string} | Redirection>}
*/
invokeMenuAction(menu, actionParams) {
return this.invokeMenuActionWithId(menu.actionId, actionParams);
}
updateSettingsWithNewDialogProperties(referringObject) {
if (referringObject) {
if (referringObject.isDialogReferrer()) {
const referringDialog = referringObject;
if (referringDialog.dialogMode) {
// @TODO - remove the uppercase conversion once all DialogModes come back from server as uppercase
this.dialogMode = referringDialog.dialogMode.toUpperCase();
}
}
}
}
/* @TODO */
writeAttachment(attachment) {
/*
return DialogService.addAttachment(this.dialogRedirection.dialogHandle, attachment, this.session);
*/
return Promise.resolve(null);
}
writeAttachments(record) {
return Promise.all(record.properties
.filter((prop) => {
return prop.value instanceof Attachment;
})
.map((prop) => {
const attachment = prop.value;
return this.writeAttachment(attachment);
}));
}
/**
* Write all Binary values in this {@link Record} back to the server
*
* @param {Record} record
* @returns {Promise<void[]>}
*/
writeLargeProperties(record) {
return Promise.all(record.properties
.filter((prop) => {
return this.propDefAtName(prop.name).isLargePropertyType;
})
.map((prop) => {
return this.writeLargeProperty(prop.name, prop.value);
}));
}
writeLargeProperty(propertyName, largeProperty) {
// This is a delete
if (!largeProperty || !largeProperty.encodedData) {
return this.catavolt.dialogApi
.writeProperty(this.tenantId, this.sessionId, this.id, propertyName, {
append: false,
encodedData: null,
type: TypeNames.WriteLargePropertyParameters
})
.then(() => Promise.resolve());
}
const data = Base64.decodeString(largeProperty.encodedData);
const f = (ptr) => {
if (ptr < data.length) {
const segment = ptr + Dialog.CHAR_CHUNK_SIZE <= data.length
? data.substr(ptr, Dialog.CHAR_CHUNK_SIZE)
: data.substring(ptr);
const params = {
append: ptr !== 0,
encodedData: Base64.encodeString(segment),
type: TypeNames.WriteLargePropertyParameters
};
return this.catavolt.dialogApi
.writeProperty(this.tenantId, this.sessionId, this.id, propertyName, params)
.then(() => {
return f(ptr + Dialog.CHAR_CHUNK_SIZE);
});
}
else {
return Promise.resolve();
}
};
return f(0);
}
/**
* @private
* @returns {boolean}
*/
get isAnyChildDestroyed() {
return (this.children &&
this.children.some((dialog) => {
return dialog.isDestroyed;
}));
}
/**
* Read a large property into memory or stream it, if a streamConsumer is provided
* @param {string} propertyName
* @param {string} recordId
* @param {StreamConsumer} streamConsumer
* @returns {Promise<LargeProperty>}
*/
loadLargeProperty(propertyName, recordId, streamConsumer) {
return Dialog.loadLargeProperty(this.getProperty.bind(this), streamConsumer, propertyName, recordId);
}
/**
* Read a large property into memory or stream it, if a streamConsumer is provided
* The actual service call that retrieves the result is delegate to the 'getPropertyFn'
* @param {(params: ReadLargePropertyParameters, propertyName?: string) => Promise<LargeProperty>} getPropertyFn
* @param {StreamConsumer} streamConsumer
* @param {string} propertyName
* @param {string} recordId
* @returns {Promise<LargeProperty>}
*/
static loadLargeProperty(getPropertyFn, streamConsumer, propertyName, recordId) {
let sequence = 0;
let resultBuffer = '';
const f = (largeProperty) => __awaiter(this, void 0, void 0, function* () {
streamConsumer &&
(yield streamConsumer({ done: !largeProperty.hasMore, value: largeProperty.encodedData }));
if (largeProperty.hasMore) {
if (!streamConsumer) {
resultBuffer += Base64.decodeString(largeProperty.encodedData);
}
const params = {
maxBytes: Dialog.BINARY_CHUNK_SIZE,
sequence: ++sequence,
recordId,
type: TypeNames.ReadLargePropertyParameters
};
return getPropertyFn(params, propertyName).then(f);
}
else {
if (resultBuffer) {
resultBuffer += Base64.decodeString(largeProperty.encodedData);
return Promise.resolve(largeProperty.asNewLargeProperty(Base64.encodeString(resultBuffer)));
}
else {
if (streamConsumer) {
return Promise.resolve(largeProperty.asNewLargeProperty(null));
}
return Promise.resolve(largeProperty.asNewLargeProperty(largeProperty.encodedData));
}
}
});
const initParams = {
maxBytes: Dialog.BINARY_CHUNK_SIZE,
sequence,
recordId,
type: TypeNames.ReadLargePropertyParameters
};
return getPropertyFn(initParams, propertyName).then(f);
}
}
// statics
Dialog.SEARCH_DIALOG_CLASS = 'SearchQueryModel';
Dialog.BINARY_CHUNK_SIZE = 128 * 1024; // size in byes for 'read' operation
Dialog.CHAR_CHUNK_SIZE = 128 * 1000; // size in chars for encoded 'write' operation