@dasf/dasf-messaging
Version:
Typescript bindings for the DASF RPC messaging protocol.
426 lines (364 loc) • 11.9 kB
text/typescript
// SPDX-FileCopyrightText: 2022-2024 Helmholtz Centre Potsdam GFZ German Research Centre for Geosciences, Potsdam, Germany
// SPDX-FileCopyrightText: 2024 Helmholtz-Zentrum hereon GmbH
//
// SPDX-License-Identifier: Apache-2.0
import {
JsonObject,
JsonProperty,
Any,
JsonConverter,
JsonCustomConvert,
} from 'json2typescript';
import type { JSONSchema7 } from 'json-schema';
/** Message types for a :class:`DASFModuleRequest` */
export enum MessageType {
Ping = 'ping',
Pong = 'pong',
Info = 'info',
ApiInfo = 'api_info',
Request = 'request',
Response = 'response',
Log = 'log',
Progress = 'progress',
}
/** Valid keys for the request properties
*
* see :attr:`DASFModuleRequest.properties`
*/
export enum PropertyKeys {
ResponseTopic = 'response_topic',
RequestContext = 'requestContext',
RequestMessageId = 'requestMessageId',
MessageType = 'messageType',
SourceTopic = 'source_topic',
Fragment = 'fragment',
NumFragments = 'num_fragments',
Status = 'status',
}
/** Status flag of a request :class:`DASFModuleRequest` */
export enum Status {
Success = 'success',
Error = 'error',
Running = 'running',
}
type DASFRequestPropertiesBase = {
messageType: MessageType;
requestContext?: string;
};
export type DASFRequestProperties = DASFRequestPropertiesBase & {
response_topic?: string;
};
export type DASFResponseProperties = DASFRequestPropertiesBase & {
requestMessageId?: string;
source_topic?: string;
fragment?: number;
num_fragments?: number;
status?: Status;
info?: string;
api_info?: string;
};
/** A request to a DASF backend module
*
* This class can be used to create a request that is sent via the message
* broker to a DASF backend module.
*/
export class DASFModuleRequest {
/** The request data.
*
* usually encoded as byte64 string.
*/
payload = '';
/** An identifier for the request to handle identify the response handler */
context = '';
/** Properties of the request
*
* the properties of the request may be derived from the
* :ref:`PropertyKeys` enum.
*/
properties?: DASFRequestProperties;
private static createMessage(type: MessageType): DASFModuleRequest {
const msg = new DASFModuleRequest();
msg.properties = {} as DASFRequestProperties;
msg.properties[PropertyKeys.MessageType] = type;
return msg;
}
/** Shortcut to create a request message
*
* This static method creates a :class:`DASFModuleRequest` with the
* :ref:`MessageType` `MessageType.Request`
*
* @param data - Optional data as javascript object that will be
* json-serialized and added as :attr:`payload` to the request.
*
* @returns The :class:`DASFModuleRequest` of type `MessageType.Request`
*/
public static createRequestMessage(data?: object): DASFModuleRequest {
const ret = DASFModuleRequest.createMessage(MessageType.Request);
if (typeof data != 'undefined') {
ret.payload = btoa(JSON.stringify(data));
}
return ret;
}
/** Shortcut to create a Ping message
*
* This static method creates a :class:`DASFModuleRequest` with the
* :ref:`MessageType` `MessageType.Ping`
*
* @returns The :class:`DASFModuleRequest` of type `MessageType.Ping`
*/
public static createPingMessage(): DASFModuleRequest {
return DASFModuleRequest.createMessage(MessageType.Ping);
}
/** Shortcut to create a Pong message
*
* This static method creates a :class:`DASFModuleRequest` with the
* :ref:`MessageType` `MessageType.Pong`
*
* @returns The :class:`DASFModuleRequest` of type `MessageType.Pong`
*/
public static createPongMessage(): DASFModuleRequest {
return DASFModuleRequest.createMessage(MessageType.Pong);
}
/** Shortcut to create an Info message
*
* This static method creates a :class:`DASFModuleRequest` with the
* :ref:`MessageType` `MessageType.Info` to get the information on the
* backend module, see also :meth:`DASFConnection.getModuleInfo`
*
* @returns The :class:`DASFModuleRequest` of type `MessageType.Info`
*/
public static createInfoMessage(): DASFModuleRequest {
return DASFModuleRequest.createMessage(MessageType.Info);
}
/** Shortcut to create an Info message
*
* This static method creates a :class:`DASFModuleRequest` with the
* :ref:`MessageType` `MessageType.Info` to get the information on the
* backend module, see also :meth:`DASFConnection.getApiInfo`
*
* @returns The :class:`DASFModuleRequest` of type `MessageType.ApiInfo`
*/
public static createApiInfoMessage(): DASFModuleRequest {
return DASFModuleRequest.createMessage(MessageType.ApiInfo);
}
/** Shortcut to the the messagetype of the properties */
public getMessageType(): MessageType {
if (this.properties) {
return this.properties[PropertyKeys.MessageType];
}
throw new Error('Missing or empty message type property');
}
}
/** An acknowledgement answer from the message broker
*
* This object is retrieved from the message broker if a request has been
* submitted successfully.
*/
export class DASFModuleRequestReceipt {
result = '';
messageId = '';
errorMsg = '';
context = '';
public isOk(): boolean {
return this.result == 'ok';
}
}
/** An acknowledgement to a response message
*
* This message can be sent to a message broker to acknowledge the receipt of
* a message.
*/
export class DASFAcknowledgment {
messageId = '';
constructor(messageId?: string) {
if (messageId) {
this.messageId = messageId;
}
}
}
/** A response of a backend module. */
export class DASFModuleResponse {
/** The id of the message assigned by the message broker */
messageId = '';
/** The response data.
*
* usually encoded as byte64 string.
*/
payload = '';
/** The time when this message has been published to the message broker. */
publishTime = '';
/** Properties of the response
*
* the properties of the response may be derived from the
* :ref:`PropertyKeys` enum.
*/
properties?: DASFResponseProperties;
/** Shortcut to the the messagetype of the properties */
public getMessageType(): MessageType {
if (this.properties) {
const msgType: MessageType = this.properties[PropertyKeys.MessageType];
if (msgType && msgType.trim().length > 0) {
return msgType;
}
}
throw new Error('Missing or empty message type property');
}
/** Shortcut to the the request context of the properties */
public getRequestContext(): string {
if (this.properties) {
const context = this.properties[PropertyKeys.RequestContext] as string;
if (context) {
return context;
}
}
throw new Error('Missing or empty request context property');
}
/** Get the original id of the request */
public getRequestMessageId(): string {
if (this.properties) {
const messageId = this.properties[
PropertyKeys.RequestMessageId
] as string;
if (messageId) {
return messageId;
}
}
throw new Error('Missing or empty request messageId property');
}
/** Check if the response is fragmented or not. */
public isFragmented(): boolean {
return (
this.properties != undefined &&
Object.prototype.hasOwnProperty.call(
this.properties,
PropertyKeys.NumFragments,
) &&
Object.prototype.hasOwnProperty.call(
this.properties,
PropertyKeys.Fragment,
)
);
}
/** Get the id of this fragment. */
public getFragmentId(): number {
if (this.properties && this.isFragmented()) {
return this.properties[PropertyKeys.Fragment] as number;
}
throw new Error(
'Unable to request fragment id from unfragmented response.',
);
}
/** Get the total number of fragments */
public getNumberOfFragments(): number {
if (this.properties && this.isFragmented()) {
return this.properties[PropertyKeys.NumFragments] as number;
}
throw new Error(
'Unable to request number of fragments from unfragmented response.',
);
}
}
/** A tree-like structured progress report.
*
* This class can be used to handle a progress report from the backend module.
* It is submitted to the `onProgress` callback of the
* :meth:`DASFConnection.sendRequest` when a response arrives with the
* :ref:`message type <MessageType>` ``MessageType.Progress``
*/
export class DASFProgressReport {
report_type = '';
/** ID for the report. */
report_id = '';
/** The description of the process. */
step_message = '';
/** The number of subprocesses in this report. */
steps = 0;
/** Status of the underlying process. */
status: Status = Status.Running;
/** Child reports within the tree */
children?: DASFProgressReport[] = undefined;
public hasError(): boolean {
return this.status == Status.Error;
}
public hasSuccess(): boolean {
return this.status == Status.Success;
}
public isRunning(): boolean {
return this.status == Status.Running;
}
public isComplete(): boolean {
return !this.isRunning();
}
}
class JsonSchemaConverter implements JsonCustomConvert<object> {
// dummy JsonSchemaConverter to make sure the json2typescript library does
// not complain
serialize(data: object): string {
return JSON.stringify(data);
}
deserialize(data: object): JSONSchema7 {
return data as JSONSchema7;
}
}
/** A function in the API suitable for RPC via DASF */
export class FunctionApiInfo {
/** The name of the function that is used as identifier in the RPC. */
name = '';
/** The JSON Schema for the function. */
rpcSchema: JSONSchema7 = {};
/** The JSON Schema for the return value. */
returnSchema: JSONSchema7 = {};
}
/** A class in the API suitable for RPC via DASF */
export class ClassApiInfo {
/** The name of the class that is used as identifier in the RPC. */
name = '';
/** The JSON Schema for the constructor of the class. */
rpcSchema: JSONSchema7 = {};
/** The list of methods that this class provides. */
methods: FunctionApiInfo[] = [];
}
/** An model that represants the API of a backend module. */
export class ModuleApiInfo {
/** The RPC-enabled classes that this module contains. */
classes: ClassApiInfo[] = [];
/** The RPC-enabled functions that this module contains. */
functions: FunctionApiInfo[] = [];
/** The aggregated JSON schema for an RPC call to this module. */
rpcSchema: JSONSchema7 = {};
}