Skip to content

Commit

Permalink
Basic Demo implementation working with parent post message
Browse files Browse the repository at this point in the history
  • Loading branch information
robmoffat committed Mar 16, 2024
1 parent beb6406 commit 884a88c
Show file tree
Hide file tree
Showing 14 changed files with 341 additions and 305 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,5 @@ Desktop Agent Briding needs extending with the following types:
## Issues To Resolve
- How does the da-server tell the da-proxy about the channel metadata?
- How does the da-server decide on a desktop agent name (maybe it just has one?)
- How does the da-server decide on a desktop agent name (maybe it just has one?)
- AppChecker / AppDetailsResolver / AppPortResolver - this is all too complex.
2 changes: 1 addition & 1 deletion packages/client/src/messaging/message-port.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DesktopAgent } from "@finos/fdc3";
import { BasicDesktopAgent, DefaultChannelSupport, DefaultAppSupport, DefaultIntentSupport, DefaultChannel, DefaultHandshakeSupport } from "da-proxy";
import { APIResponseMessage, FDC3_PORT_TRANSFER_RESPONSE_TYPE, FDC3_PORT_TRANSFER_REQUEST_TYPE, Options, exchangeForMessagePort, exchange } from "fdc3-common"
import { APIResponseMessage, FDC3_PORT_TRANSFER_RESPONSE_TYPE, FDC3_PORT_TRANSFER_REQUEST_TYPE, Options, exchangeForMessagePort } from "fdc3-common"
import { MessagePortMessaging } from "./MessagePortMessaging";
import { DesktopAgentIntentResolver } from "../intent-resolution/DesktopAgentIntentResolver";

Expand Down
16 changes: 16 additions & 0 deletions packages/da-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ServerContext } from "./ServerContext"
import { BasicFDC3Server, DefaultFDC3Server } from "./BasicFDC3Server"
import { FDC3Server } from "./FDC3Server"
import { Directory, DirectoryApp, DirectoryIntent } from "./directory/DirectoryInterface"
import { BasicDirectory } from "./directory/BasicDirectory"

export {
type ServerContext,
BasicFDC3Server,
DefaultFDC3Server,
type FDC3Server,
type Directory,
BasicDirectory,
type DirectoryApp,
type DirectoryIntent
}
172 changes: 172 additions & 0 deletions packages/fdc3-web-demo/src/client/da/DemoServerContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { AppMetadata } from '@finos/fdc3/dist/bridging/BridgingTypes';
import { Directory, DirectoryApp, ServerContext } from 'da-server'
import { Socket, io } from 'socket.io-client';
import { v4 as uuid } from 'uuid'
import { APP_HELLO, FDC3_DA_EVENT } from '../../message-types';
import { AppIdentifier, OpenError } from '@finos/fdc3';
import { AppChecker, DesktopAgentDetailResolver, DesktopAgentDetails, DesktopAgentPortResolver } from 'fdc3-common';
import { link } from './util';

enum Opener { Tab, Frame, Nested }

type AppRegistration = {
appId: AppIdentifier,
window: Window
url: string
}

enum Approach { IFRAME, PARENT_POST_MESSAGE }

function getApproach(): Approach {
const cb = document.getElementById("approach") as HTMLInputElement;
const val = cb.value
var out: Approach = Approach[val as keyof typeof Approach]; //Works with --noImplicitAny
return out;
}

export class DemoServerContext implements ServerContext {

private readonly socket: Socket
private readonly directory: Directory
private readonly desktopAgentUUID: string

private instances: AppRegistration[] = []

constructor(socket: Socket, directory: Directory, desktopAgentUUID: string) {
this.socket = socket
this.directory = directory
this.desktopAgentUUID = desktopAgentUUID
}

getOpener(): Opener {
const cb = document.getElementById("opener") as HTMLInputElement;
const val = cb.value
var out: Opener = Opener[val as keyof typeof Opener]; //Works with --noImplicitAny
return out;
}

createUUID(): string {
return uuid()
}

async post(message: object, to: AppMetadata): Promise<void> {
console.log(`Responding with: ${JSON.stringify(message, null, 2)} to ${JSON.stringify(to, null, 2)}`)
this.socket.emit(FDC3_DA_EVENT, message, to)
}

openFrame(url: string): Window {
var ifrm = document.createElement("iframe");
ifrm.setAttribute("src", url);
ifrm.style.width = "640px";
ifrm.style.height = "480px";
document.body.appendChild(ifrm);
return ifrm.contentWindow!!;
}

openTab(url: string): Window {
return window.open(url, "_blank")!!;
}

openNested(url: string): Window {
var ifrm = document.createElement("iframe");
ifrm.setAttribute("src", "nested.html?url=" + url);
ifrm.style.width = "640px";
ifrm.style.height = "480px";
document.body.appendChild(ifrm);
return ifrm.contentWindow!!;
}

openUrl(url: string): Window {
const opener = this.getOpener();
switch (opener) {
case Opener.Tab:
return this.openTab(url);
case Opener.Nested:
return this.openNested(url);
case Opener.Frame:
return this.openFrame(url);
}
throw new Error("unsupported")
}

async open(appId: string): Promise<AppMetadata> {
const details = this.directory.retrieveAppsById(appId) as DirectoryApp[]
if (details.length > 0) {
const url = details[0].details.url
const window = this.openUrl(url)
const metadata = {
appId,
instanceId: this.createUUID()
}
this.instances.push({
appId: metadata,
url,
window
})

return metadata
}

throw new Error(OpenError.AppNotFound)
}

async getOpenApps(): Promise<AppMetadata[]> {
return this.instances.map(i => i.appId)
}

async isAppOpen(app: AppMetadata): Promise<boolean> {
return (await this.getOpenApps()).filter(ai =>
(ai.appId == app.appId) && (ai.instanceId == app.instanceId)).length > 0
}

log(message: string): void {
console.log(message);
}

provider(): string {
return "FDC3-Web-Demo"
}

providerVersion(): string {
return "0.1"
}

fdc3Version(): string {
return "2.0"
}

// for a given window, allows us to determine which app it is (if any)
appChecker: AppChecker = o => this.instances.find(i => i.window == o)?.appId

// this is for when the API is using an iframe, and needs to know the address to load
detailsResolver: DesktopAgentDetailResolver = (o: Window, a: AppIdentifier) => {
const apiKey = "ABC"
if (getApproach() == Approach.IFRAME) {
return {
apiKey,
uri: window.location.origin + "/static/embed/index.html"
}
} else {
return {
apiKey
} as DesktopAgentDetails
}
}

portResolver: DesktopAgentPortResolver = (o: Window, a: AppIdentifier) => {
if (getApproach() == Approach.IFRAME) {
return null;
} else {
const channel = new MessageChannel()
const socket = io()

socket.on("connect", () => {
console.log("Server creating socket")
link(socket, channel, a)
socket.emit(APP_HELLO, this.desktopAgentUUID, a)
})

return channel.port1;
}
}
}
10 changes: 2 additions & 8 deletions packages/fdc3-web-demo/src/client/da/FDC3_2_1_JSONDirectory.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import { BasicDirectory } from "da-server/src/directory/BasicDirectory";
import { DirectoryApp } from "da-server/src/directory/DirectoryInterface";
import { readFile } from 'fs/promises';

function loadRemotely(u: string) {
return fetch(u).then((response) => response.json());
}

function loadLocally(u: string) {
return readFile(u)
.then((buf) => buf.toString('utf8'))
.then((data) => JSON.parse(data));
}

async function load(url: string): Promise<DirectoryApp[]> {
if (url.startsWith('http')) {
return await loadRemotely(url).then(convertToDirectoryList);
} else {
return await loadLocally(url).then(convertToDirectoryList);
return await loadRemotely(window.location.origin + url).then(convertToDirectoryList);

}
}
const convertToDirectoryList = (data: any) => {
Expand Down
19 changes: 0 additions & 19 deletions packages/fdc3-web-demo/src/client/da/constants.ts

This file was deleted.

Loading

0 comments on commit 884a88c

Please sign in to comment.