From f0656ce6ae05d087373a33c8f4f4a84fb778fbc0 Mon Sep 17 00:00:00 2001 From: Dinh Long Nguyen Date: Mon, 10 Jun 2024 16:55:03 +0700 Subject: [PATCH] Get Routes with type frontend decouple (#1370) --- frontend/taipy-gui/base/src/app.ts | 23 ++++++--- frontend/taipy-gui/base/src/utils.ts | 60 ++++++++++++----------- frontend/taipy-gui/src/context/wsUtils.ts | 3 +- taipy/gui/gui.py | 20 ++++++++ taipy/gui/page.py | 2 + taipy/gui/types.py | 1 + 6 files changed, 73 insertions(+), 36 deletions(-) diff --git a/frontend/taipy-gui/base/src/app.ts b/frontend/taipy-gui/base/src/app.ts index 59c789281d..83aa4a70b1 100644 --- a/frontend/taipy-gui/base/src/app.ts +++ b/frontend/taipy-gui/base/src/app.ts @@ -6,29 +6,31 @@ import { Socket, io } from "socket.io-client"; import { DataManager, ModuleData } from "./dataManager"; import { initSocket } from "./utils"; -export type OnInitHandler = (appManager: TaipyApp) => void; -export type OnChangeHandler = (appManager: TaipyApp, encodedName: string, value: unknown) => void; -export type OnNotifyHandler = (appManager: TaipyApp, type: string, message: string) => void; -export type onReloadHandler = (appManager: TaipyApp, removedChanges: ModuleData) => void; +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; +type Route = [string, string]; export class TaipyApp { socket: Socket; _onInit: OnInitHandler | undefined; _onChange: OnChangeHandler | undefined; _onNotify: OnNotifyHandler | undefined; - _onReload: onReloadHandler | undefined; + _onReload: OnReloadHandler | undefined; variableData: DataManager | undefined; functionData: DataManager | undefined; appId: string; clientId: string; context: string; path: string | undefined; + routes: Route[] | undefined; constructor( onInit: OnInitHandler | undefined = undefined, onChange: OnChangeHandler | undefined = undefined, path: string | undefined = undefined, - socket: Socket | undefined = undefined + socket: Socket | undefined = undefined, ) { socket = socket || io("/", { autoConnect: false }); this.onInit = onInit; @@ -38,6 +40,7 @@ export class TaipyApp { this.clientId = ""; this.context = ""; this.appId = ""; + this.routes = undefined; this.path = path; this.socket = socket; initSocket(socket, this); @@ -77,7 +80,7 @@ export class TaipyApp { get onReload() { return this._onReload; } - set onReload(handler: onReloadHandler | undefined) { + set onReload(handler: OnReloadHandler | undefined) { if (handler !== undefined && handler?.length !== 2) { throw new Error("_onReload() requires two parameters"); } @@ -89,9 +92,11 @@ export class TaipyApp { this.clientId = ""; this.context = ""; this.appId = ""; + this.routes = undefined; const id = getLocalStorageValue(TAIPY_CLIENT_ID, ""); sendWsMessage(this.socket, "ID", TAIPY_CLIENT_ID, id, id, undefined, false); sendWsMessage(this.socket, "AID", "connect", "", id, undefined, false); + sendWsMessage(this.socket, "GR", "", "", id, undefined, false); if (id !== "") { this.clientId = id; this.updateContext(this.path); @@ -128,6 +133,10 @@ export class TaipyApp { return Object.keys(functionData || {}); } + getRoutes() { + return this.routes; + } + // This update will only send the request to Taipy Gui backend // the actual update will be handled when the backend responds update(encodedName: string, value: unknown) { diff --git a/frontend/taipy-gui/base/src/utils.ts b/frontend/taipy-gui/base/src/utils.ts index 836bda06e2..9f943efa93 100644 --- a/frontend/taipy-gui/base/src/utils.ts +++ b/frontend/taipy-gui/base/src/utils.ts @@ -17,16 +17,16 @@ interface AlertMessage extends WsMessage { const initWsMessageTypes = ["ID", "AID", "GMC"]; -export const initSocket = (socket: Socket, appManager: TaipyApp) => { +export const initSocket = (socket: Socket, taipyApp: TaipyApp) => { socket.on("connect", () => { - if (appManager.clientId === "" || appManager.appId === "") { - appManager.init(); + 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", () => { console.log("WebSocket reconnected") - sendWsMessage(socket, "AID", "reconnect", appManager.appId, appManager.clientId, appManager.context); + sendWsMessage(socket, "AID", "reconnect", taipyApp.appId, taipyApp.clientId, taipyApp.context); }); // try to reconnect on connect_error socket.on("connect_error", (err) => { @@ -44,7 +44,7 @@ export const initSocket = (socket: Socket, appManager: TaipyApp) => { }); // handle message data from backend socket.on("message", (message: WsMessage) => { - processWsMessage(message, appManager); + processWsMessage(message, taipyApp); }); // only now does the socket tries to open/connect if (!socket.connected) { @@ -52,62 +52,66 @@ export const initSocket = (socket: Socket, appManager: TaipyApp) => { } }; -const processWsMessage = (message: WsMessage, appManager: TaipyApp) => { +const processWsMessage = (message: WsMessage, taipyApp: TaipyApp) => { if (message.type) { if (message.type === "MU" && Array.isArray(message.payload)) { for (const muPayload of message.payload as [MultipleUpdatePayload]) { const encodedName = muPayload.name; const { value } = muPayload.payload; - appManager.variableData?.update(encodedName, value); - appManager.onChange && appManager.onChange(appManager, encodedName, value); + taipyApp.variableData?.update(encodedName, value); + taipyApp.onChange && taipyApp.onChange(taipyApp, encodedName, value); } } else if (message.type === "ID") { const { id } = message as unknown as IdMessage; storeClientId(id); - appManager.clientId = id; - appManager.updateContext(appManager.path); + taipyApp.clientId = id; + taipyApp.updateContext(taipyApp.path); } else if (message.type === "GMC") { const mc = (message.payload as Record).data as string; window.localStorage.setItem("ModuleContext", mc); - appManager.context = mc; + taipyApp.context = mc; } else if (message.type === "GDT") { const payload = message.payload as Record; const variableData = payload.variable; const functionData = payload.function; - if (appManager.variableData && appManager.functionData) { - const varChanges = appManager.variableData.init(variableData); - const functionChanges = appManager.functionData.init(functionData); + if (taipyApp.variableData && taipyApp.functionData) { + const varChanges = taipyApp.variableData.init(variableData); + const functionChanges = taipyApp.functionData.init(functionData); const changes = merge(varChanges, functionChanges); if (varChanges || functionChanges) { - appManager.onReload && appManager.onReload(appManager, changes); + taipyApp.onReload && taipyApp.onReload(taipyApp, changes); } } else { - appManager.variableData = new DataManager(variableData); - appManager.functionData = new DataManager(functionData); - appManager.onInit && appManager.onInit(appManager); + taipyApp.variableData = new DataManager(variableData); + taipyApp.functionData = new DataManager(functionData); + taipyApp.onInit && taipyApp.onInit(taipyApp); } } else if (message.type === "AID") { const payload = message.payload as Record; if (payload.name === "reconnect") { - return appManager.init(); + return taipyApp.init(); } - appManager.appId = payload.id as string; - } else if (message.type === "AL" && appManager.onNotify) { + taipyApp.appId = payload.id as string; + } else if (message.type === "GR") { + const payload = message.payload as [string, string][]; + taipyApp.routes = payload; + } else if (message.type === "AL" && taipyApp.onNotify) { const payload = message as AlertMessage; - appManager.onNotify(appManager, payload.atype, payload.message); + taipyApp.onNotify(taipyApp, payload.atype, payload.message); } - postWsMessageProcessing(message, appManager); + postWsMessageProcessing(message, taipyApp); } }; -const postWsMessageProcessing = (message: WsMessage, appManager: TaipyApp) => { +const postWsMessageProcessing = (message: WsMessage, taipyApp: TaipyApp) => { // perform data population only when all necessary metadata is ready if ( initWsMessageTypes.includes(message.type) && - appManager.clientId !== "" && - appManager.appId !== "" && - appManager.context !== "" + taipyApp.clientId !== "" && + taipyApp.appId !== "" && + taipyApp.context !== "" && + taipyApp.routes !== undefined ) { - sendWsMessage(appManager.socket, "GDT", "get_data_tree", {}, appManager.clientId, appManager.context); + sendWsMessage(taipyApp.socket, "GDT", "get_data_tree", {}, taipyApp.clientId, taipyApp.context); } }; diff --git a/frontend/taipy-gui/src/context/wsUtils.ts b/frontend/taipy-gui/src/context/wsUtils.ts index 66af1fe37a..6ac7c67a29 100644 --- a/frontend/taipy-gui/src/context/wsUtils.ts +++ b/frontend/taipy-gui/src/context/wsUtils.ts @@ -19,7 +19,8 @@ export type WsMessageType = | "ACK" | "GMC" | "GDT" - | "AID"; + | "AID" + | "GR"; export interface WsMessage { type: WsMessageType; diff --git a/taipy/gui/gui.py b/taipy/gui/gui.py index d956e41e05..0aec438dc7 100644 --- a/taipy/gui/gui.py +++ b/taipy/gui/gui.py @@ -630,6 +630,8 @@ def _manage_message(self, msg_type: _WsType, message: dict) -> None: self.__handle_ws_get_data_tree() elif msg_type == _WsType.APP_ID.value: self.__handle_ws_app_id(message) + elif msg_type == _WsType.GET_ROUTES.value: + self.__handle_ws_get_routes() self.__send_ack(message.get("ack_id")) except Exception as e: # pragma: no cover if isinstance(e, AttributeError) and (name := message.get("name")): @@ -1167,6 +1169,24 @@ def __handle_ws_app_id(self, message: t.Any): } ) + def __handle_ws_get_routes(self): + routes = ( + [[self._config.root_page._route, self._config.root_page._renderer.page_type]] + if self._config.root_page + else [] + ) + routes += [ + [page._route, page._renderer.page_type] + for page in self._config.pages + if page._route != Gui.__root_page_name + ] + self.__send_ws( + { + "type": _WsType.GET_ROUTES.value, + "payload": routes, + } + ) + def __send_ws(self, payload: dict, allow_grouping=True, send_back_only=False) -> None: grouping_message = self.__get_message_grouping() if allow_grouping else None if grouping_message is None: diff --git a/taipy/gui/page.py b/taipy/gui/page.py index 1b6feb28fa..1c5598fd7e 100644 --- a/taipy/gui/page.py +++ b/taipy/gui/page.py @@ -34,6 +34,8 @@ class Page: your application variables and interact with them. """ + page_type: str = "Taipy" + def __init__(self, **kwargs) -> None: from .custom import Page as CustomPage diff --git a/taipy/gui/types.py b/taipy/gui/types.py index b734bb7374..743440c842 100644 --- a/taipy/gui/types.py +++ b/taipy/gui/types.py @@ -49,6 +49,7 @@ class _WsType(Enum): ACKNOWLEDGEMENT = "ACK" GET_MODULE_CONTEXT = "GMC" GET_DATA_TREE = "GDT" + GET_ROUTES = "GR" NumberTypes = {"int", "int64", "float", "float64"}