Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Commit

Permalink
Add exchange state machine
Browse files Browse the repository at this point in the history
  • Loading branch information
Diane Huxley committed Feb 9, 2024
1 parent 552675c commit ced180a
Show file tree
Hide file tree
Showing 4 changed files with 515 additions and 1 deletion.
122 changes: 122 additions & 0 deletions packages/protocol/src/exchange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Close, Order, OrderStatus, Quote, Rfq } from './message-kinds/index.js'
import { Message } from './message.js'
import { MessageKind } from './types.js'

/**
* State-machine for validating the order and metadata of Tbdex messages in an exchange.
*
* This state-machine does not validate the {@link Message.signature} or {@link Message.data}
* of messages in the exchange.
*
* Either add messages in order one at a time using {@link Exchange.addNextMessage},
* or add a list of unsorted messages in an exchange using {@link Exchange.addMessages}
*/
export class Exchange {

Check warning on line 14 in packages/protocol/src/exchange.ts

View workflow job for this annotation

GitHub Actions / tbdocs-reporter

extractor: ae-missing-release-tag

"Exchange" is part of the package's API, but it is missing a release tag (`@alpha`, `@beta`, `@public`, or `@internal`)
/** Message sent by Alice to PFI to request a quote */
rfq: Rfq | undefined
/** Message sent by the PFI in response to an RFQ */
quote: Quote | undefined
/** Message sent by Alice to the PFI to accept a quote*/
order: Order | undefined
/** Message sent by the PFI to Alice to convet the current status of the order */
orderstatus: OrderStatus | undefined
/** Message sent by either the PFI or Alice to terminate an exchange */
close: Close | undefined

constructor() {}

/**
* Add a list of unsorted messages to an exchange.
* @param messages - An unsorted array of Tbdex messages in a given exchange
*/
addMessages(messages: Message[]): void {
// Sort with earliest dateCreated first
const sortedMessages = messages.sort((m1, m2) => {
const time1 = new Date(m1.metadata.createdAt).getTime()
const time2 = new Date(m2.metadata.createdAt).getTime()
return time1 - time2
})

for (const message of sortedMessages) {
this.addNextMessage(message)
}
}

/**
* Add the next message in the exchange
* @param message - The next allowed message in the exchange
* @throws if message is not a valid next message. See {@link Exchange.isValidNext}
*/
addNextMessage(message: Message): void {
if (!this.isValidNext(message.metadata.kind)) {
throw new Error(
`Could not add message (${message.metadata.id}) to exchange because ${message.metadata.kind} ` +
`is not a valid next message`
)
}

if (this.exchangeId !== undefined && message.metadata.exchangeId !== this.exchangeId) {
throw new Error(
`Could not add message with id ${message.metadata.id} to exchange because it does not have matching ` +
`exchange id ${this.exchangeId}`
)
}

if (message.isRfq()) {
this.rfq = message
} else if (message.isQuote()) {
this.quote = message
} else if (message.isClose()) {
this.close = message
} else if (message.isOrder()) {
this.order = message
} else if (message.isOrderStatus()) {
this.orderstatus = message
} else {
// Unreachable
throw new Error('Unrecognized message kind')
}

Check warning on line 78 in packages/protocol/src/exchange.ts

View check run for this annotation

Codecov / codecov/patch

packages/protocol/src/exchange.ts#L76-L78

Added lines #L76 - L78 were not covered by tests
}

/**
* Determines if the message kind is a valid next message in the current exchange
* @param messageKind

Check warning on line 83 in packages/protocol/src/exchange.ts

View workflow job for this annotation

GitHub Actions / tbdocs-reporter

docs: tsdoc-param-tag-missing-hyphen

The `@param` block should be followed by a parameter name and then a hyphen
* @returns true if the next message in the exchange may have kind messageKind, false otherwise
*/
isValidNext(messageKind: MessageKind): boolean {
const validNext = this.latestMessage?.validNext ?? new Set<MessageKind>(['rfq'])
return validNext.has(messageKind)
}

/**
* @returns Latest message in an exchange if there are any messages currently
*/
get latestMessage(): Message | undefined {

Check warning on line 94 in packages/protocol/src/exchange.ts

View workflow job for this annotation

GitHub Actions / tbdocs-reporter

extractor: ae-undocumented

Missing documentation for "latestMessage".
return this.close ??
this.orderstatus ??
this.order ??
this.quote ??
this.rfq
}

/**
* @returns The exchangeId of all messages in the Exchange
*/
get exchangeId(): string | undefined {

Check warning on line 105 in packages/protocol/src/exchange.ts

View workflow job for this annotation

GitHub Actions / tbdocs-reporter

extractor: ae-undocumented

Missing documentation for "exchangeId".
return this.rfq?.metadata?.exchangeId
}

/**
* @returns A sorted list of messages currently in the exchange.
*/
get messages(): Message[] {

Check warning on line 112 in packages/protocol/src/exchange.ts

View workflow job for this annotation

GitHub Actions / tbdocs-reporter

extractor: ae-undocumented

Missing documentation for "messages".
const allPossibleMessages: (Message | undefined)[] = [
this.rfq,
this.quote,
this.order,
this.orderstatus,
this.close
]
return allPossibleMessages.filter((message): message is Message => message !== undefined)
}
}
1 change: 1 addition & 0 deletions packages/protocol/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Message } from './message.js'

export * from './resource-kinds/index.js'
export * from './message-kinds/index.js'
export * from './exchange.js'
export * from './did-resolver.js'
export * from './dev-tools.js'
export * from './crypto.js'
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/src/message-kinds/rfq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type CreateRfqOptions = {
}

/**
* Message sent by Alice to PFI to requesting for a quote (RFQ)
* Message sent by Alice to PFI to request a quote (RFQ)
* @beta
*/
export class Rfq extends Message {
Expand Down
Loading

0 comments on commit ced180a

Please sign in to comment.