Skip to content

Commit

Permalink
lint in fdc3-web-impl
Browse files Browse the repository at this point in the history
  • Loading branch information
kriswest committed Dec 11, 2024
1 parent 0ea5b95 commit 731b2ec
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Connectable } from '@kite9/fdc3-standard';

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface HeartbeatSupport extends Connectable {}
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@
},
"LaunchDetails": {
"description": "The type specific launch details of the application. These details are intended to be vendor-agnostic and MAY be duplicated or overridden by details provided in the hostManifests object for a specific host.",
"anyOf": [
"oneOf": [
{
"$ref": "#/components/schemas/WebAppDetails"
},
Expand All @@ -675,6 +675,7 @@
]
},
"WebAppDetails": {
"type": "object",
"description": "Properties used to launch apps with `type: web`.",
"required": [
"url"
Expand All @@ -689,6 +690,7 @@
"additionalProperties": false
},
"NativeAppDetails": {
"type": "object",
"description": "Properties used to launch apps with `type: native` that are already installed on the device.",
"required": [
"path"
Expand All @@ -706,6 +708,7 @@
"additionalProperties": false
},
"CitrixAppDetails": {
"type": "object",
"description": "Properties used to launch apps virtualized apps with `type: citrix`.",
"required": [
"alias"
Expand All @@ -723,6 +726,7 @@
"additionalProperties": false
},
"OnlineNativeAppDetails": {
"type": "object",
"description": "Properties used to launch a native apps with `type: onlineNative` that have an online launcher, e.g. online ClickOnce app deployments.",
"required": [
"url"
Expand All @@ -737,7 +741,9 @@
"additionalProperties": false
},
"OtherAppDetails": {
"type": "object",
"description": "Apps with `type: other` are defined by a hostManifest and do not require other details.",
"properties": {},
"additionalProperties": false
},
"HostManifests": {
Expand Down
103 changes: 68 additions & 35 deletions toolbox/fdc3-for-web/demo/src/client/da/DemoServerContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AppRegistration, Directory, DirectoryApp, InstanceID, ServerContext, St
import { Socket } from 'socket.io-client';
import { v4 as uuid } from 'uuid';
import { FDC3_DA_EVENT } from '../../message-types';
import { AppIdentifier, AppIntent, Context, OpenError } from '@kite9/fdc3';
import { AppIdentifier, AppIntent, OpenError } from '@kite9/fdc3';

enum Opener {
Tab,
Expand All @@ -15,6 +15,11 @@ type DemoRegistration = AppRegistration & {
url: string;
};

//Typeguard used to check if application launch details have a URL
function isWebAppLaunchDetails(details: object): details is { url: string } {
return (details as { url: string }).url !== undefined;
}

export class DemoServerContext implements ServerContext<DemoRegistration> {
private readonly socket: Socket;
private readonly directory: Directory;
Expand All @@ -25,7 +30,7 @@ export class DemoServerContext implements ServerContext<DemoRegistration> {
this.directory = directory;
}

async narrowIntents(_raiser: AppIdentifier, appIntents: AppIntent[], _context: Context): Promise<AppIntent[]> {
async narrowIntents(_raiser: AppIdentifier, appIntents: AppIntent[] /*, _context: Context*/): Promise<AppIntent[]> {
return appIntents;
}

Expand All @@ -48,7 +53,7 @@ export class DemoServerContext implements ServerContext<DemoRegistration> {
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
const out: Opener = Opener[val as keyof typeof Opener]; //Works with --noImplicitAny
return out;
}

Expand All @@ -64,15 +69,6 @@ export class DemoServerContext implements ServerContext<DemoRegistration> {
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!!;
}

goodbye(id: string) {
this.connections = this.connections.filter(i => i.instanceId !== id);
console.debug(`Closed instance`, id);
Expand All @@ -82,20 +78,42 @@ export class DemoServerContext implements ServerContext<DemoRegistration> {
);
}

openTab(url: string): Window {
return window.open(url, '_blank')!!;
openFrame(url: string): Promise<Window | null> {
const iframe = document.createElement('iframe');
iframe.setAttribute('src', url);
iframe.style.width = '640px';
iframe.style.height = '480px';
document.body.appendChild(iframe);

//wait for load event, after which contentWindow should not be null
const loadPromise = new Promise<Window | null>(resolve => {
iframe.onload = () => resolve(iframe.contentWindow);
});
return loadPromise;
}

openTab(url: string): Promise<Window | null> {
//n.b. There are cases where the window reference returned is null
// That can happen if the Cross-Origin-Opener-Policy opener policy is set (see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy)
// or a browser pop-up blocker gets in the way...
return Promise.resolve(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!!;
openNested(url: string): Promise<Window | null> {
const iframe = document.createElement('iframe');
iframe.setAttribute('src', 'nested.html?url=' + url);
iframe.style.width = '640px';
iframe.style.height = '480px';
document.body.appendChild(iframe);

//wait for load event, after which contentWindow should not be null
const loadPromise = new Promise<Window | null>(resolve => {
iframe.onload = () => resolve(iframe.contentWindow);
});
return loadPromise;
}

openUrl(url: string): Window {
async openUrl(url: string): Promise<Window | null> {
const opener = this.getOpener();
switch (opener) {
case Opener.Tab:
Expand All @@ -110,19 +128,34 @@ export class DemoServerContext implements ServerContext<DemoRegistration> {
async open(appId: string): Promise<InstanceID> {
const details: DirectoryApp[] = this.directory.retrieveAppsById(appId) as DirectoryApp[];
if (details.length > 0) {
const url = (details[0].details as any)?.url ?? undefined;
const window = this.openUrl(url);
const instanceId: InstanceID = this.createUUID();
const metadata = {
appId,
instanceId,
window,
url,
state: State.Pending,
};

this.setInstanceDetails(instanceId, metadata);
return instanceId;
const launchDetails = details[0].details;
if (isWebAppLaunchDetails(launchDetails)) {
const url = launchDetails.url ?? undefined;
const window = await this.openUrl(url);
if (window) {
const instanceId: InstanceID = this.createUUID();
const metadata = {
appId,
instanceId,
window,
url,
state: State.Pending,
};

this.setInstanceDetails(instanceId, metadata);
return instanceId;
} else {
console.error(
'We did not receive a window reference after launching app: ',
details[0],
'\nn.b. this may occur if a popup blocker prevented launch or the Cross-Origin-Opener-Policy opener policy is set'
);
throw new Error(OpenError.ErrorOnLaunch);
}
} else {
console.error('Unable to launch app without a URL, app: ', details[0]);
throw new Error(OpenError.ErrorOnLaunch);
}
}

throw new Error(OpenError.AppNotFound);
Expand Down
6 changes: 3 additions & 3 deletions toolbox/fdc3-for-web/fdc3-web-impl/src/ServerContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export type AppRegistration = {
/**
* This is a unique, long, unguessable string that identifies a particular instance of an app.
* All messages arriving at the desktop agent will have this UUID attached to them.
* It is important that this is unguessable as it is a "password" of sorts used to
* identify the app between reconnections.
* It is important that this is unguessable as it is a shared secret used to identify the app
* when reconnecting after navigation or refresh.
*/
export type InstanceID = string;

Expand Down Expand Up @@ -71,7 +71,7 @@ export interface ServerContext<X extends AppRegistration> {
getConnectedApps(): Promise<AppRegistration[]>;

/**
* Return the list of all apps that have ever been registed with the ServerContext.
* Return the list of all apps that have ever been registered with the ServerContext.
*/
getAllApps(): Promise<AppRegistration[]>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ export class BasicDirectory implements Directory {
const lfAugmented = lfa.map(([key, value]) => {
return {
intentName: key,
...(value as any),
...value,
appId: a.appId,
};
});
return lfAugmented as DirectoryIntent[];
return lfAugmented;
}

retrieveAllIntents(): DirectoryIntent[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,8 @@ export class BroadcastHandler implements MessageHandler {
}

handleAddContextListenerRequest(arg0: AddContextListenerRequest, sc: ServerContext<any>, from: FullAppIdentifier) {
var channelId = null;
var channelType = ChannelType.user;
let channelId = null;
let channelType = ChannelType.user;

if (arg0.payload?.channelId) {
const channel = this.getChannelById(arg0.payload?.channelId);
Expand Down Expand Up @@ -395,7 +395,7 @@ export class BroadcastHandler implements MessageHandler {

handleGetOrCreateRequest(arg0: GetOrCreateChannelRequest, sc: ServerContext<any>, from: FullAppIdentifier) {
const id = arg0.payload.channelId;
var channel = this.getChannelById(id);
let channel = this.getChannelById(id);
if (channel) {
if (channel.type != ChannelType.app) {
errorResponse(sc, arg0, from, ChannelError.AccessDenied, 'getOrCreateChannelResponse');
Expand Down Expand Up @@ -462,7 +462,6 @@ export class BroadcastHandler implements MessageHandler {
sc: ServerContext<any>,
contextType?: string
) {
console.log('invokePrivateChannelEventListeners', arguments);
if (privateChannelId) {
const msg: PrivateChannelEvents = {
type: messageType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class HeartbeatHandler implements MessageHandler {
this.sendHeartbeat(sc, app);

// check when the last heartbeat happened
const lastHeartbeat = this.lastHeartbeats.get(app.instanceId!!);
const lastHeartbeat = this.lastHeartbeats.get(app.instanceId);
const currentState = app.state;

if (lastHeartbeat != undefined) {
Expand All @@ -61,23 +61,23 @@ export class HeartbeatHandler implements MessageHandler {
console.error(
`Heartbeat from ${app.instanceId} for ${timeSinceLastHeartbeat}ms. App is considered connected.`
);
sc.setAppState(app.instanceId!!, State.Connected);
sc.setAppState(app.instanceId, State.Connected);
} else if (timeSinceLastHeartbeat > disconnectedAfter && currentState == State.Connected) {
console.error(
`No heartbeat from ${app.instanceId} for ${timeSinceLastHeartbeat}ms. App is considered not responding.`
);
sc.setAppState(app.instanceId!!, State.NotResponding);
sc.setAppState(app.instanceId, State.NotResponding);
} else if (timeSinceLastHeartbeat > deadAfter && currentState == State.NotResponding) {
console.error(
`No heartbeat from ${app.instanceId} for ${timeSinceLastHeartbeat}ms. App is considered terminated.`
);
sc.setAppState(app.instanceId!!, State.Terminated);
sc.setAppState(app.instanceId, State.Terminated);
} else {
// no action
}
} else {
// start the clock
this.lastHeartbeats.set(app.instanceId!!, now);
this.lastHeartbeats.set(app.instanceId, now);
}
});
});
Expand Down Expand Up @@ -113,7 +113,7 @@ export class HeartbeatHandler implements MessageHandler {
if (msg.type == 'heartbeatAcknowledgementRequest') {
const app = sc.getInstanceDetails(from);
if (app) {
this.lastHeartbeats.set(app.instanceId!!, new Date().getTime());
this.lastHeartbeats.set(app.instanceId, new Date().getTime());
}
}

Expand Down
Loading

0 comments on commit 731b2ec

Please sign in to comment.