UNPKG

cv-dialog-sdk

Version:

Catavolt Dialog Javascript API

401 lines (400 loc) 14.3 kB
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