Skip to content

Commit

Permalink
Expose websocket decouple frontend (#1501) (#1521)
Browse files Browse the repository at this point in the history
* Expose websocket decouple frontend

* add useEvent per Fred

* per Fred and Fabien
  • Loading branch information
dinhlongviolin1 authored Jul 16, 2024
1 parent 2c9d6e4 commit dd7f26e
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 18 deletions.
45 changes: 44 additions & 1 deletion frontend/taipy-gui/base/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ export type OnInitHandler = (taipyApp: TaipyApp) => void;
export type OnChangeHandler = (taipyApp: TaipyApp, encodedName: string, value: unknown) => void;
export type OnNotifyHandler = (taipyApp: TaipyApp, type: string, message: string) => void;
export type OnReloadHandler = (taipyApp: TaipyApp, removedChanges: ModuleData) => void;
export type OnWsMessage = (taipyApp: TaipyApp, event: string, payload: unknown) => void;
export type OnWsStatusUpdate = (taipyApp: TaipyApp, messageQueue: string[]) => void;
export type OnEvent =
| OnInitHandler
| OnChangeHandler
| OnNotifyHandler
| OnReloadHandler
| OnWsMessage
| OnWsStatusUpdate;
type Route = [string, string];

export class TaipyApp {
Expand All @@ -21,6 +29,7 @@ export class TaipyApp {
_onChange: OnChangeHandler | undefined;
_onNotify: OnNotifyHandler | undefined;
_onReload: OnReloadHandler | undefined;
_onWsMessage: OnWsMessage | undefined;
_onWsStatusUpdate: OnWsStatusUpdate | undefined;
_ackList: string[];
variableData: DataManager | undefined;
Expand Down Expand Up @@ -69,6 +78,10 @@ export class TaipyApp {
this._onInit = handler;
}

onInitEvent() {
this.onInit && this.onInit(this);
}

get onChange() {
return this._onChange;
}
Expand All @@ -80,6 +93,10 @@ export class TaipyApp {
this._onChange = handler;
}

onChangeEvent(encodedName: string, value: unknown) {
this.onChange && this.onChange(this, encodedName, value);
}

get onNotify() {
return this._onNotify;
}
Expand All @@ -91,6 +108,10 @@ export class TaipyApp {
this._onNotify = handler;
}

onNotifyEvent(type: string, message: string) {
this.onNotify && this.onNotify(this, type, message);
}

get onReload() {
return this._onReload;
}
Expand All @@ -101,6 +122,24 @@ export class TaipyApp {
this._onReload = handler;
}

onReloadEvent(removedChanges: ModuleData) {
this.onReload && this.onReload(this, removedChanges);
}

get onWsMessage() {
return this._onWsMessage;
}
set onWsMessage(handler: OnWsMessage | undefined) {
if (handler !== undefined && handler?.length !== 3) {
throw new Error("onWsMessage() requires three parameters");
}
this._onWsMessage = handler;
}

onWsMessageEvent(event: string, payload: unknown) {
this.onWsMessage && this.onWsMessage(this, event, payload);
}

get onWsStatusUpdate() {
return this._onWsStatusUpdate;
}
Expand All @@ -111,6 +150,10 @@ export class TaipyApp {
this._onWsStatusUpdate = handler;
}

onWsStatusUpdateEvent(messageQueue: string[]) {
this.onWsStatusUpdate && this.onWsStatusUpdate(this, messageQueue);
}

// Utility methods
init() {
this.clientId = "";
Expand All @@ -134,7 +177,7 @@ export class TaipyApp {
const ackId = sendWsMessage(this.socket, type, id, payload, this.clientId, context);
if (ackId) {
this._ackList.push(ackId);
this.onWsStatusUpdate && this.onWsStatusUpdate(this, this._ackList);
this.onWsStatusUpdateEvent(this._ackList);
}
}

Expand Down
31 changes: 21 additions & 10 deletions frontend/taipy-gui/base/src/packaging/taipy-gui-base.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,22 @@ export type WsMessageType =
| "GMC"
| "GDT"
| "AID"
| "GR";
| "GR"
| "FV";
export interface WsMessage {
type: WsMessageType | string;
name: string;
payload: Record<string, unknown> | unknown;
propagate: boolean;
client_id: string;
module_context: string;
ack_id?: string;
}
export type OnInitHandler = (taipyApp: TaipyApp) => void;
export type OnChangeHandler = (taipyApp: TaipyApp, encodedName: string, value: unknown) => void;
export type OnNotifyHandler = (taipyApp: TaipyApp, type: string, message: string) => void;
export type OnReloadHandler = (taipyApp: TaipyApp, removedChanges: ModuleData) => void;
export type OnWsMessage = (taipyApp: TaipyApp, event: string, payload: unknown) => void;
export type OnWsStatusUpdate = (taipyApp: TaipyApp, messageQueue: string[]) => void;
export type Route = [string, string];
export declare class TaipyApp {
Expand All @@ -50,6 +61,7 @@ export declare class TaipyApp {
_onChange: OnChangeHandler | undefined;
_onNotify: OnNotifyHandler | undefined;
_onReload: OnReloadHandler | undefined;
_onWsMessage: OnWsMessage | undefined;
_onWsStatusUpdate: OnWsStatusUpdate | undefined;
_ackList: string[];
variableData: DataManager | undefined;
Expand All @@ -69,14 +81,22 @@ export declare class TaipyApp {
);
get onInit(): OnInitHandler | undefined;
set onInit(handler: OnInitHandler | undefined);
onInitEvent(): void;
get onChange(): OnChangeHandler | undefined;
set onChange(handler: OnChangeHandler | undefined);
onChangeEvent(encodedName: string, value: unknown): void;
get onNotify(): OnNotifyHandler | undefined;
set onNotify(handler: OnNotifyHandler | undefined);
onNotifyEvent(type: string, message: string): void;
get onReload(): OnReloadHandler | undefined;
set onReload(handler: OnReloadHandler | undefined);
onReloadEvent(removedChanges: ModuleData): void;
get onWsMessage(): OnWsMessage | undefined;
set onWsMessage(handler: OnWsMessage | undefined);
onWsMessageEvent(event: string, payload: unknown): void;
get onWsStatusUpdate(): OnWsStatusUpdate | undefined;
set onWsStatusUpdate(handler: OnWsStatusUpdate | undefined);
onWsStatusUpdateEvent(messageQueue: string[]): void;
init(): void;
sendWsMessage(type: WsMessageType | string, id: string, payload: unknown, context?: string | undefined): void;
registerWsAdapter(wsAdapter: WsAdapter): void;
Expand All @@ -96,15 +116,6 @@ export declare class TaipyApp {
getPageMetadata(): Record<string, unknown>;
getWsStatus(): string[];
}
export interface WsMessage {
type: WsMessageType | string;
name: string;
payload: Record<string, unknown> | unknown;
propagate: boolean;
client_id: string;
module_context: string;
ack_id?: string;
}
export declare abstract class WsAdapter {
abstract supportedMessageTypes: string[];
abstract handleWsMessage(message: WsMessage, app: TaipyApp): boolean;
Expand Down
5 changes: 5 additions & 0 deletions frontend/taipy-gui/base/src/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,36 @@ import { TaipyApp } from "./app";

export const initSocket = (socket: Socket, taipyApp: TaipyApp) => {
socket.on("connect", () => {
taipyApp.onWsMessageEvent("connect", null);
if (taipyApp.clientId === "" || taipyApp.appId === "") {
taipyApp.init();
}
});
// Send a request to get App ID to verify that the app has not been reloaded
socket.io.on("reconnect", () => {
taipyApp.onWsMessageEvent("reconnect", null);
console.log("WebSocket reconnected");
taipyApp.sendWsMessage("AID", "reconnect", taipyApp.appId);
});
// try to reconnect on connect_error
socket.on("connect_error", (err) => {
taipyApp.onWsMessageEvent("connect_error", { err });
console.log("Error connecting WebSocket: ", err);
setTimeout(() => {
socket && socket.connect();
}, 500);
});
// try to reconnect on server disconnection
socket.on("disconnect", (reason, details) => {
taipyApp.onWsMessageEvent("disconnect", { reason, details });
console.log("WebSocket disconnected due to: ", reason, details);
if (reason === "io server disconnect") {
socket && socket.connect();
}
});
// handle message data from backend
socket.on("message", (message: WsMessage) => {
taipyApp.onWsMessageEvent("message", message);
// handle messages with registered websocket adapters
for (const adapter of taipyApp.wsAdapters) {
if (adapter.supportedMessageTypes.includes(message.type)) {
Expand Down
14 changes: 7 additions & 7 deletions frontend/taipy-gui/base/src/wsAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class TaipyWsAdapter extends WsAdapter {
const encodedName = muPayload.name;
const { value } = muPayload.payload;
taipyApp.variableData?.update(encodedName, value);
taipyApp.onChange && taipyApp.onChange(taipyApp, encodedName, value);
taipyApp.onChangeEvent(encodedName, value);
}
} else if (message.type === "ID") {
const { id } = message as unknown as IdMessage;
Expand All @@ -61,12 +61,12 @@ export class TaipyWsAdapter extends WsAdapter {
const functionChanges = taipyApp.functionData.init(functionData);
const changes = merge(varChanges, functionChanges);
if (varChanges || functionChanges) {
taipyApp.onReload && taipyApp.onReload(taipyApp, changes);
taipyApp.onReloadEvent(changes);
}
} else {
taipyApp.variableData = new DataManager(variableData);
taipyApp.functionData = new DataManager(functionData);
taipyApp.onInit && taipyApp.onInit(taipyApp);
taipyApp.onInitEvent();
}
} else if (message.type === "AID") {
const payload = message.payload as Record<string, unknown>;
Expand All @@ -78,13 +78,13 @@ export class TaipyWsAdapter extends WsAdapter {
} else if (message.type === "GR") {
const payload = message.payload as [string, string][];
taipyApp.routes = payload;
} else if (message.type === "AL" && taipyApp.onNotify) {
} else if (message.type === "AL") {
const payload = message as AlertMessage;
taipyApp.onNotify(taipyApp, payload.atype, payload.message);
taipyApp.onNotifyEvent(payload.atype, payload.message);
} else if (message.type === "ACK") {
const {id} = message as unknown as Record<string, string>;
const { id } = message as unknown as Record<string, string>;
taipyApp._ackList = taipyApp._ackList.filter((v) => v !== id);
taipyApp.onWsStatusUpdate && taipyApp.onWsStatusUpdate(taipyApp, taipyApp._ackList);
taipyApp.onWsStatusUpdateEvent(taipyApp._ackList);
}
this.postWsMessageProcessing(message, taipyApp);
return true;
Expand Down

0 comments on commit dd7f26e

Please sign in to comment.