diff --git a/README.md b/README.md index 10c3365e..a88d6b28 100644 --- a/README.md +++ b/README.md @@ -283,7 +283,7 @@ Port uses the following data model (also see: [src/framework/types](src/framewor | ProcessingEngine | Responsible for processing donation flows | | VisualizationEngine | Responsible for presenting the UI and accepting user input | | CommandHandler | Decoupling of ProcessingEngine and VisualizationEngine | - | Storage | Callback interface for Storage Commands (e.g. Donation) | + | Bridge | Callback interface for Bridge Commands (e.g. Donation) | - [Pages](src/framework/types/pages.ts) @@ -345,7 +345,7 @@ See: [src/framework/processing/py/port](src/framework/processing/py/port) - [API](src/framework/processing/py/port/api) - - [commands.py](src/framework/processing/py/port/api/commands.py): Defines commands, pages and prompts that are used to communicate from the Python script to the `VisualisationEngine` and `Storage`. + - [commands.py](src/framework/processing/py/port/api/commands.py): Defines commands, pages and prompts that are used to communicate from the Python script to the `VisualisationEngine` and `Bridge`. - [props.py](src/framework/processing/py/port/api/commands.py): Defines property objects for pages and prompts ## Code instructions @@ -542,7 +542,7 @@ Change implementation of [assembly.ts](src/framework/assembly.ts) to support you ```Typescript import MyEngine from './visualisation/my/engine' import WorkerProcessingEngine from './processing/worker_engine' -import { VisualisationEngine, ProcessingEngine, Storage } from './types/modules' +import { VisualisationEngine, ProcessingEngine, Bridge } from './types/modules' import CommandRouter from './command_router' export default class Assembly { @@ -550,7 +550,7 @@ export default class Assembly { processingEngine: ProcessingEngine router: CommandRouter - constructor (worker: Worker, system: Storage) { + constructor (worker: Worker, bridge: Bridge) { const sessionId = String(Date.now()) this.visualisationEngine = new MyEngine() this.router = new CommandRouter(system, this.visualisationEngine) diff --git a/public/port-0.0.0-py3-none-any.whl b/public/port-0.0.0-py3-none-any.whl index c4593e28..dc81166b 100644 Binary files a/public/port-0.0.0-py3-none-any.whl and b/public/port-0.0.0-py3-none-any.whl differ diff --git a/src/fake_bridge.ts b/src/fake_bridge.ts new file mode 100644 index 00000000..d930f8c3 --- /dev/null +++ b/src/fake_bridge.ts @@ -0,0 +1,22 @@ +import { CommandSystem, CommandSystemDonate, CommandSystemExit, isCommandSystemDonate, isCommandSystemExit } from './framework/types/commands' +import { Bridge } from './framework/types/modules' + +export default class FakeBridge implements Bridge { + send (command: CommandSystem): void { + if (isCommandSystemDonate(command)) { + this.handleDonation(command) + } else if (isCommandSystemExit(command)) { + this.handleExit(command) + } else { + console.log('[FakeBridge] received unknown command: ' + JSON.stringify(command)) + } + } + + handleDonation (command: CommandSystemDonate): void { + console.log(`[FakeBridge] received donation: ${command.key}=${command.json_string}`) + } + + handleExit (command: CommandSystemExit): void { + console.log(`[FakeBridge] received exit: ${command.code}=${command.info}`) + } +} diff --git a/src/fake_storage.ts b/src/fake_storage.ts deleted file mode 100644 index 605be9b0..00000000 --- a/src/fake_storage.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CommandSystem, CommandSystemDonate, isCommandSystemDonate } from './framework/types/commands' -import { Storage } from './framework/types/modules' - -export default class FakeStorage implements Storage { - send (command: CommandSystem): void { - if (isCommandSystemDonate(command)) { - this.handleDonation(command) - } else { - console.log('[LocalSystem] received unknown command: ' + JSON.stringify(command)) - } - } - - handleDonation (command: CommandSystemDonate): void { - console.log(`[LocalSystem] received donation: ${command.key}=${command.json_string}`) - } -} diff --git a/src/framework/assembly.ts b/src/framework/assembly.ts index 8a02ec7c..a121b62d 100644 --- a/src/framework/assembly.ts +++ b/src/framework/assembly.ts @@ -1,7 +1,7 @@ import ReactEngine from './visualisation/react/engine' import ReactFactory from './visualisation/react/factory' import WorkerProcessingEngine from './processing/worker_engine' -import { VisualisationEngine, ProcessingEngine, Storage } from './types/modules' +import { VisualisationEngine, ProcessingEngine, Bridge } from './types/modules' import CommandRouter from './command_router' export default class Assembly { @@ -9,10 +9,10 @@ export default class Assembly { processingEngine: ProcessingEngine router: CommandRouter - constructor (worker: Worker, system: Storage) { + constructor (worker: Worker, bridge: Bridge) { const sessionId = String(Date.now()) this.visualisationEngine = new ReactEngine(new ReactFactory()) - this.router = new CommandRouter(system, this.visualisationEngine) + this.router = new CommandRouter(bridge, this.visualisationEngine) this.processingEngine = new WorkerProcessingEngine(sessionId, worker, this.router) } } diff --git a/src/framework/command_router.ts b/src/framework/command_router.ts index efe0de58..e3c51145 100644 --- a/src/framework/command_router.ts +++ b/src/framework/command_router.ts @@ -1,12 +1,12 @@ import { Command, Response, isCommandSystem, isCommandUI, CommandUI, CommandSystem } from './types/commands' -import { CommandHandler, Storage, VisualisationEngine } from './types/modules' +import { CommandHandler, Bridge, VisualisationEngine } from './types/modules' export default class CommandRouter implements CommandHandler { - system: Storage + bridge: Bridge visualisationEngine: VisualisationEngine - constructor (system: Storage, visualisationEngine: VisualisationEngine) { - this.system = system + constructor (bridge: Bridge, visualisationEngine: VisualisationEngine) { + this.bridge = bridge this.visualisationEngine = visualisationEngine } @@ -23,7 +23,7 @@ export default class CommandRouter implements CommandHandler { } onCommandSystem (command: CommandSystem, resolve: (response: Response) => void): void { - this.system.send(command) + this.bridge.send(command) resolve({ __type__: 'Response', command, payload: { __type__: 'PayloadVoid', value: undefined } }) } diff --git a/src/framework/processing/py/dist/port-0.0.0-py3-none-any.whl b/src/framework/processing/py/dist/port-0.0.0-py3-none-any.whl index c4593e28..dc81166b 100644 Binary files a/src/framework/processing/py/dist/port-0.0.0-py3-none-any.whl and b/src/framework/processing/py/dist/port-0.0.0-py3-none-any.whl differ diff --git a/src/framework/processing/py/port/api/commands.py b/src/framework/processing/py/port/api/commands.py index 3bcb4cc5..e7c11188 100644 --- a/src/framework/processing/py/port/api/commands.py +++ b/src/framework/processing/py/port/api/commands.py @@ -24,3 +24,18 @@ def toDict(self): dict["key"] = self.key dict["json_string"] = self.json_string return dict + + +class CommandSystemExit: + __slots__ = "code", "info" + + def __init__(self, code, info): + self.code = code + self.info = info + + def toDict(self): + dict = {} + dict["__type__"] = "CommandSystemExit" + dict["code"] = self.code + dict["info"] = self.info + return dict diff --git a/src/framework/processing/py/port/script.py b/src/framework/processing/py/port/script.py index 7abb7d71..35386d7d 100644 --- a/src/framework/processing/py/port/script.py +++ b/src/framework/processing/py/port/script.py @@ -1,5 +1,5 @@ import port.api.props as props -from port.api.commands import (CommandSystemDonate, CommandUIRender) +from port.api.commands import (CommandSystemDonate, CommandSystemExit, CommandUIRender) import pandas as pd import zipfile @@ -58,6 +58,7 @@ def process(sessionId): meta_data.append(("debug", f"{platform}: donate consent data")) yield donate(f"{sessionId}-{platform}", consent_result.value) + yield exit(0, "Success") yield render_end_page() @@ -141,3 +142,6 @@ def prompt_consent(id, data, meta_data): def donate(key, json_string): return CommandSystemDonate(key, json_string) + +def exit(code, info): + return CommandSystemExit(code, info) diff --git a/src/framework/types/commands.ts b/src/framework/types/commands.ts index f8ff58a0..096e28f4 100644 --- a/src/framework/types/commands.ts +++ b/src/framework/types/commands.ts @@ -82,10 +82,11 @@ export function isCommand (arg: any): arg is Command { } export type CommandSystem = - CommandSystemDonate + CommandSystemDonate | + CommandSystemExit export function isCommandSystem (arg: any): arg is CommandSystem { - return isCommandSystemDonate(arg) + return isCommandSystemDonate(arg) || isCommandSystemExit(arg) } export type CommandUI = @@ -104,6 +105,15 @@ export function isCommandSystemDonate (arg: any): arg is CommandSystemDonate { return isInstanceOf(arg, 'CommandSystemDonate', ['key', 'json_string']) } +export interface CommandSystemExit { + __type__: 'CommandSystemExit' + code: number + info: string +} +export function isCommandSystemExit (arg: any): arg is CommandSystemExit { + return isInstanceOf(arg, 'CommandSystemExit', ['code', 'info']) +} + export interface CommandUIRender { __type__: 'CommandUIRender' page: PropsUIPage diff --git a/src/framework/types/modules.ts b/src/framework/types/modules.ts index ccf10358..f460b30d 100644 --- a/src/framework/types/modules.ts +++ b/src/framework/types/modules.ts @@ -12,7 +12,7 @@ export interface VisualisationEngine { terminate: () => void } -export interface Storage { +export interface Bridge { send: (command: CommandSystem) => void } diff --git a/src/index.tsx b/src/index.tsx index acf5e1b9..af027173 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,30 +1,37 @@ import './fonts.css' import './framework/styles.css' import Assembly from './framework/assembly' -import { Storage } from './framework/types/modules' -import LiveStorage from './live_storage' -import FakeStorage from './fake_storage' +import { Bridge } from './framework/types/modules' +import LiveBridge from './live_bridge' +import FakeBridge from './fake_bridge' const rootElement = document.getElementById('root') as HTMLElement -const locale = 'en' const workerFile = new URL('./framework/processing/py_worker.js', import.meta.url) const worker = new Worker(workerFile) let assembly: Assembly -const run = (system: Storage): void => { - assembly = new Assembly(worker, system) +const run = (bridge: Bridge, locale: string): void => { + assembly = new Assembly(worker, bridge) assembly.visualisationEngine.start(rootElement, locale) assembly.processingEngine.start() } -if (process.env.REACT_APP_BUILD!=='standalone' && process.env.NODE_ENV === 'production') { +if (process.env.REACT_APP_BUILD !== 'standalone' && process.env.NODE_ENV === 'production') { // Setup embedded mode (requires to be embedded in iFrame) - console.log('Initializing storage system') - LiveStorage.create(window, run) + console.log('Initializing bridge system') + LiveBridge.create(window, run) } else { // Setup local development mode - console.log('Running with fake storage') - run(new FakeStorage()) + console.log('Running with fake bridge') + run(new FakeBridge(), 'en') } + +const observer = new ResizeObserver(() => { + const height = window.document.body.scrollHeight; + const action = "resize" + window.parent.postMessage({action, height}, "*") +}); + +observer.observe(window.document.body); diff --git a/src/live_bridge.ts b/src/live_bridge.ts new file mode 100644 index 00000000..4aa16d4d --- /dev/null +++ b/src/live_bridge.ts @@ -0,0 +1,36 @@ +import { CommandSystem, isCommandSystem } from './framework/types/commands' +import { Bridge } from './framework/types/modules' + +export default class LiveBridge implements Bridge { + port: MessagePort + + constructor (port: MessagePort) { + this.port = port + } + + static create (window: Window, callback: (bridge: Bridge, locale: string) => void): void { + window.addEventListener('message', (event) => { + console.log('MESSAGE RECEIVED', event) + // Skip webpack messages + if (event.data.action === 'live-init') { + const bridge = new LiveBridge(event.ports[0]) + const locale = event.data.locale + console.log('LOCALE', locale) + callback(bridge, locale) + } + }) + } + + send (command: CommandSystem): void { + if (isCommandSystem(command)) { + this.port.postMessage(command) + } else { + this.log('error', 'received unknown command', command) + } + } + + private log (level: 'info' | 'error', ...message: any[]): void { + const logger = level === 'info' ? console.log : console.error + logger(`[${this.constructor.name}]`, ...message) + } +} diff --git a/src/live_storage.ts b/src/live_storage.ts deleted file mode 100644 index c1f05d4b..00000000 --- a/src/live_storage.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { CommandSystem, CommandSystemDonate, isCommandSystemDonate } from './framework/types/commands' -import { Storage } from './framework/types/modules' - -export default class LiveStorage implements Storage { - port: MessagePort - - constructor (port: MessagePort) { - this.port = port - } - - static create (window: Window, callback: (system: Storage) => void): void { - window.addEventListener('message', (event) => { - console.log('MESSAGE RECEIVED', event) - // Skip webpack messages - if (event.ports.length === 0) { - return - } - const system = new LiveStorage(event.ports[0]) - callback(system) - }) - } - - send (command: CommandSystem): void { - if (isCommandSystemDonate(command)) { - this.port.postMessage(command) - } else { - this.log('error', 'received unknown command', command) - } - } - - handleDonation (command: CommandSystemDonate): void { - this.log('info', `received donation: ${command.key}=${command.json_string}`) - } - - private log (level: 'info' | 'error', ...message: any[]): void { - const logger = level === 'info' ? console.log : console.error - logger(`[${this.constructor.name}]`, ...message) - } -}