Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose websocket decouple frontend #1501

Merged
merged 7 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
dinhlongviolin1 marked this conversation as resolved.
Show resolved Hide resolved
}

onChangeEvent(encodedName: string, value: unknown) {
this.onChange && this.onChange(this, encodedName, value);
dinhlongviolin1 marked this conversation as resolved.
Show resolved Hide resolved
}

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() {
dinhlongviolin1 marked this conversation as resolved.
Show resolved Hide resolved
return this._onWsMessage;
}
set onWsMessage(handler: OnWsMessage | undefined) {
if (handler !== undefined && handler?.length !== 3) {
throw new Error("onWsMessage() requires three parameters");
}
this._onWsMessage = handler;
dinhlongviolin1 marked this conversation as resolved.
Show resolved Hide resolved
}

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
Loading