Skip to content

Commit

Permalink
First intent tests working
Browse files Browse the repository at this point in the history
  • Loading branch information
robmoffat committed Mar 8, 2024
1 parent 11df194 commit 75bbfe2
Show file tree
Hide file tree
Showing 17 changed files with 451 additions and 117 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ packages/da/tsconfig.tsbuildinfo
packages/testing/tsconfig.tsbuildinfo
**/coverage
.nyc_output
packages/da-server/generated
2 changes: 2 additions & 0 deletions packages/da-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "da-server",
"main": "src/index.ts",
"scripts": {
"directory-openapi": "npx openapi-typescript https://fdc3.finos.org/schemas/2.1/appd.schema.json --output generated/directory-schema.ts",
"build": "nyc --reporter=lcov --reporter=text cucumber-js"
},
"dependencies": {
Expand Down Expand Up @@ -29,6 +30,7 @@
"is-ci": "2.0.0",
"jsonpath-plus": "^7.2.0",
"nyc": "15.1.0",
"openapi-typescript": "^6.7.4",
"prettier": "2.2.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.2",
Expand Down
15 changes: 9 additions & 6 deletions packages/da-server/src/BasicFDC3Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ import { AgentRequestMessage } from "@finos/fdc3/dist/bridging/BridgingTypes";
import { FDC3Server } from "./FDC3Server";
import { ServerContext } from "./ServerContext";
import { BroadcastHandler } from "./handlers/BroadcastHandler";
import { IntentHandler } from "./handlers/IntentHandler";
import { Directory } from "./directory/DirectoryInterface";

export interface MessageHandler {

accept(msg: AgentRequestMessage, sc: ServerContext) : void
accept(msg: AgentRequestMessage, sc: ServerContext): void
}

/**
* This defers all functionality to either MessageHandler's or the ServerContext objects.
*/
export class BasicFDC3Server implements FDC3Server {

private handlers : MessageHandler[]
private handlers: MessageHandler[]
private sc: ServerContext

constructor(handlers : MessageHandler[], sc: ServerContext) {
constructor(handlers: MessageHandler[], sc: ServerContext) {
this.handlers = handlers
this.sc = sc;
}
Expand All @@ -25,14 +27,15 @@ export class BasicFDC3Server implements FDC3Server {
this.sc.log(`MessageReceived: \n ${JSON.stringify(message, null, 2)}`)
this.handlers.forEach(h => h.accept(message, this.sc))
}

}

export class DefaultFDC3Server extends BasicFDC3Server {

constructor(sc: ServerContext) {
constructor(sc: ServerContext, directory: Directory) {
super([
new BroadcastHandler()
new BroadcastHandler(),
new IntentHandler(directory)
], sc)
}
}
78 changes: 78 additions & 0 deletions packages/da-server/src/directory/BasicDirectory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Directory, DirectoryApp, DirectoryIntent } from "./DirectoryInterface"

/**
* Basic directory implementation that allows queries over a set of apps.
*/
export class BasicDirectory implements Directory {

allApps: DirectoryApp[]

constructor(apps: DirectoryApp[]) {
this.allApps = apps
}

/** For retrieving intents */

retrieveIntents(contextType: string | undefined, intentName: string | undefined, resultType: string | undefined): DirectoryIntent[] {
return this.retrieveAllIntents().filter(i => this.intentMatches(i, contextType, intentName, resultType))
}

intentMatches(i: DirectoryIntent, contextType: string | undefined, intentName: string | undefined, resultType: string | undefined): boolean {
return ((intentName == undefined) || (i.intentName == intentName)) &&
((contextType == undefined) || (i.contexts.includes(contextType))) &&
((resultType == undefined) || (i.resultType == resultType) || (genericResultType(i.resultType) == resultType))
}

retrieveAllIntents(): DirectoryIntent[] {
return this.allApps.flatMap(a => this.retrieveIntentsForApp(a))
}

retrieveIntentsForApp(a: DirectoryApp): DirectoryIntent[] {
const lf = a.interop?.intents?.listensFor ?? {}
const lfa = Object.entries(lf)
const lfAugmented = lfa.map(([key, value]) => {
return {
intentName: key,
...value,
appId: a.appId
}
})
return lfAugmented
}


/** For retrieving apps */

retrieveApps(contextType: string | undefined, intentName: string | undefined, resultType: string | undefined): DirectoryApp[] {
return this.retrieveAllApps()
.filter(a => this.retrieveIntentsForApp(a)
.filter(i => this.intentMatches(i, contextType, intentName, resultType))
.length > 0)
}

retrieveAppsById(appId: string): DirectoryApp[] {
return this.retrieveAllApps().filter(a => a.appId == appId)
}

retrieveAllApps(): DirectoryApp[] {
return this.allApps
}

/**
* For FDC3 1.2, retreives by the name of the app
*/
retrieveAppsByName(name: string): DirectoryApp[] {
return this.retrieveAllApps().filter(a => a.name == name)
}
}


function genericResultType(rt: string | undefined): string | undefined {
if (rt == undefined) {
return undefined;
} else if (rt.indexOf('channel<') == 0) {
return 'channel';
} else {
return rt;
}
}
36 changes: 36 additions & 0 deletions packages/da-server/src/directory/DirectoryInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { components } from '../directory-schema'

type schemas = components['schemas']

export type DirectoryIntent = schemas['Intent'] & { intentName: string, appId: string }
export type DirectoryApp = schemas['Application'];


/**
* This interface wraps the functionality of the FDC3 Directory structure (stored in JSON),
* providing lookup calls to functions that would be handled by inspecting the directory/directories JSON definitions.
*/

export interface Directory {

/** For retrieving intents */

//retrieveIntents(contextType: string | undefined, intentName: string | undefined, resultType: string | undefined): DirectoryIntent[]

retrieveAllIntents(): DirectoryIntent[]


/** For retrieving apps */

retrieveApps(contextType: string | undefined, intentName: string | undefined, resultType: string | undefined): DirectoryApp[]

retrieveAppsById(appId: string): DirectoryApp[]

retrieveAllApps(): DirectoryApp[]

/**
* For FDC3 1.2, retreives by the name of the app
*/
retrieveAppsByName(name: string): DirectoryApp[]

}
10 changes: 10 additions & 0 deletions packages/da-server/src/directory/fdc3-2.1-json-directory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Directory } from "./DirectoryInterface";

/**
* Handles loading of JSON from URLs
*/
export class FDC32_1JSONDirectory implements Directory {



}
32 changes: 16 additions & 16 deletions packages/da-server/src/handlers/BroadcastHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@ type ListenerRegistration = {
contextType: string | null
}

function matches(lr1: ListenerRegistration, lr2: ListenerRegistration) : boolean {
function matches(lr1: ListenerRegistration, lr2: ListenerRegistration): boolean {
return (lr1.appId == lr2.appId) &&
(lr1.instanceId == lr2.instanceId) &&
(lr1.channelId == lr2.channelId) &&
(lr1.channelId == lr2.channelId) &&
(lr1.contextType == lr2.contextType)
}

function createListenerRegistration(msg:
PrivateChannelOnAddContextListenerAgentRequest |
PrivateChannelOnUnsubscribeAgentRequest) : ListenerRegistration {
function createListenerRegistration(msg:
PrivateChannelOnAddContextListenerAgentRequest |
PrivateChannelOnUnsubscribeAgentRequest): ListenerRegistration {

return {
appId: msg.meta.source?.appId!!,
instanceId: msg.meta.source?.instanceId!!,
channelId: msg.payload.channelId,
contextType: msg.payload.contextType
}
return {
appId: msg.meta.source?.appId!!,
instanceId: msg.meta.source?.instanceId!!,
channelId: msg.payload.channelId,
contextType: msg.payload.contextType
}
}

export class BroadcastHandler implements MessageHandler {

private regs: ListenerRegistration[] = []
accept(msg: AgentRequestMessage, sc: ServerContext) {
switch(msg.type as string /* 1165, see below */) {

accept(msg: AgentRequestMessage, sc: ServerContext) {
switch (msg.type as string /* 1165, see below */) {
case 'PrivateChannel.broadcast': return this.handleBroadcast(msg as PrivateChannelBroadcastAgentRequest, sc)
case 'PrivateChannel.onAddContextListener': return this.handleOnAddContextListener(msg as PrivateChannelOnAddContextListenerAgentRequest, sc)
case 'PrivateChannel.onUnsubscribe': return this.handleOnUnsubscribe(msg as PrivateChannelOnUnsubscribeAgentRequest, sc)
Expand Down Expand Up @@ -82,11 +82,11 @@ export class BroadcastHandler implements MessageHandler {
},
type: arg0.type,
payload: arg0.payload
}
} as PrivateChannelBroadcastAgentRequest

sc.post(out)
})
}
}
}


54 changes: 54 additions & 0 deletions packages/da-server/src/handlers/IntentHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { AgentRequestMessage, FindIntentAgentRequest, FindIntentAgentResponse } from "@finos/fdc3/dist/bridging/BridgingTypes";
import { MessageHandler } from "../BasicFDC3Server";
import { ServerContext } from "../ServerContext";
import { Directory } from "../directory/DirectoryInterface";

export class IntentHandler implements MessageHandler {

private readonly directory: Directory

constructor(d: Directory) {
this.directory = d
}


accept(msg: AgentRequestMessage, sc: ServerContext): void {
switch (msg.type as string) {
case 'findIntentRequest': return this.findIntentRequest(msg as FindIntentAgentRequest, sc)
}
}

findIntentRequest(r: FindIntentAgentRequest, sc: ServerContext): void {
const { intent, context, resultType } = r.payload

const apps = this.directory.retrieveApps(context?.type, intent, resultType)
.map(a => {
return {
appId: a.appId
}
})

const out = {
meta: {
requestUuid: r.meta.requestUuid,
timestamp: new Date(),
responseUuid: sc.createUUID()
},
type: "findIntentResponse",
payload: {
appIntent: {
intent: {
name: r.payload.intent
},
apps
}
}
} as FindIntentAgentResponse

sc.post(out)
}




}
30 changes: 0 additions & 30 deletions packages/da-server/test/features/broadcast.feature

This file was deleted.

19 changes: 19 additions & 0 deletions packages/da-server/test/features/intents.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Feature: Responding to Directory Requests about Intents

Background:
Given "libraryApp" is an app with the following intents
| Intent Name | Context Type | Result Type |
| loanBook | fdc3.book | fdc3.loan |
And A newly instantiated FDC3 Server

Scenario: Failed Find Intents Request
When "App1/a1" finds intents with intent "loanBook" and contextType "fdc3.instrument" and result type "{empty}"
Then messaging will have outgoing posts
| type | payload.appIntent.intent.name | payload.appIntent.apps.length |
| findIntentResponse | loanBook | 0 |

Scenario: Successful Find Intents Request
When "App1/a1" finds intents with intent "loanBook" and contextType "{empty}" and result type "{empty}"
Then messaging will have outgoing posts
| type | payload.appIntent.intent.name | payload.appIntent.apps.length | payload.appIntent.apps[0].appId |
| findIntentResponse | loanBook | 1 | libraryApp |
Loading

0 comments on commit 75bbfe2

Please sign in to comment.