diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4bed256..7669187 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,6 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - # - uses: WebAssembly/wit-abi-up-to-date@v6 - # with: - # wit-abi-tag: wit-abi-0.6.0 + - uses: WebAssembly/wit-abi-up-to-date@v17 + with: + wit-bindgen: '0.34.0' + worlds: 'imports imports-request-reply messaging-core messaging-request-reply' diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 496ee2c..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index f66d066..1d303b8 100644 --- a/README.md +++ b/README.md @@ -2,47 +2,103 @@ A proposed [WebAssembly System Interface](https://github.com/WebAssembly/WASI) API. -### Current Phase +## Table of Contents + +- [`wasi-messaging`](#wasi-messaging) + - [Table of Contents](#table-of-contents) + - [Current Phase](#current-phase) + - [Champions](#champions) + - [Phase 4 Advancement Criteria](#phase-4-advancement-criteria) + - [Introduction](#introduction) + - [Goals](#goals) + - [Portability criteria](#portability-criteria) + - [Dev notes](#dev-notes) -`wasi-messaging` is currently in [Phase 1](https://github.com/WebAssembly/WASI/blob/main/Proposals.md#phase-1---feature-proposal-cg). +## Current Phase -### Champions +`wasi-messaging` is currently in [Phase +1](https://github.com/WebAssembly/WASI/blob/main/Proposals.md#phase-1---feature-proposal-cg). + +## Champions - [Dan Chiarlone](https://github.com/danbugs) - [David Justice](https://github.com/devigned) - [Jiaxiao Zhou](https://github.com/Mossaka) +- [Taylor Thomas](https://github.com/thomastaylor312) -### Phase 4 Advancement Criteria +## Phase 4 Advancement Criteria -`wasi-messaging` should have at least two implementations (i.e., from service providers, and or cloud providers), and, at the very minimum, pass the testsuite for Windows, Linux, and MacOS. +For `wasi-messaging` to advance to Phase 4, it must have at least two independent implementations +for open-source message brokers (such as Kafka, NATS, MQTT brokers) and two for cloud service providers +(e.g., Azure Service Bus, AWS SQS). -## Table of Contents +## Introduction + +In modern software systems, different components or applications often need to communicate with each other +to exchange information and coordinate their actions. Messaging systems facilitate this communication by +allowing messages to be sent and received between different parts of a system. + +However, implementing message-based communication can be challenging. It requires dealing with the details +of message brokers, such as connection management, channel setup, and message serialization. This complexity +can hinder development and lead to inconsistent implementations. + +The `wasi-messaging` interface is a purposefully small interface focused purely on message passing. It is +designed to express the bare minimum of receiving a message and sending a message, along with an optional +request/reply interface that allows for message-based APIs and/or RPC. + +By providing a standard way to interact with message brokers, the `wasi-messaging` interface aims to simplify +this process, hiding the underlying complexity from the user. This aligns with the broader goals of WASI by +promoting interoperability, modularity, and security in WebAssembly applications. -- [Introduction](#introduction) -- [Goals](#goals) -- [Non-goals](#non-goals) +## Goals -### Introduction +The primary goal of this interface is to focus on message passing. The only guarantee offered is +that publishing a message is handled successfully. No other guarantees are made about the delivery +of the message or being able to ack/nack a message directly. This minimalist approach provides the +most basic foundation of messaging, which can be expanded upon by future interfaces or proposals as +use cases emerge. -The messaging interfaces aim to provide a generic and flexible way for producers and consumers to communicate through message brokers. The `producer` interface allows producers to publish events to a specific channel in a broker, while the `consumer` interface allows consumers to subscribe to a channel and receive events through a push-based mechanism. The handler interface provides an on-receive function that can be used to process received events with full abstraction of the underlying broker implementation. +This simplicity allows: +- **Ease of Integration**: Components can easily implement the message handler in this interface, +with details such as work dispatch and queuing handled behind the scenes, invisible to the business logic. +- **Flexibility**: Anything that can send a message can easily be passed into a queue +(such as a Kafka stream or NATS JetStream) without the knowledge that it is being sent into a queue. +- **Extensibility**: The paradigm can be expanded by future interfaces (like a queue-based work interface) to handle +more complex messaging scenarios. By focusing solely on message passing, the wasi-messaging interface simplifies the +development of interoperable WebAssembly modules that can communicate over various messaging systems without being +tied to any specific implementation. -### Goals +## Portability criteria -The messaging service interfaces aim to address the need for a standard way to handle message-based communication in modern software systems. In complex software systems, different components or even different applications need to communicate with each other to exchange information and coordinate their actions. +The main portability criteria on which this should be judged is whether a component can receive and send a message +from all major messaging systems. This includes: +- Message standards like MQTT and AMQP. +- Specific technologies like NATS and Kafka. +- Cloud provider implementations like Azure Service Bus and AWS SQS. -However, implementing message-based communication can be challenging, as it requires dealing with the details of message brokers, such as connection management, channel setup, and message serialization. The messaging service interfaces aim to simplify this process by providing a standard way to interact with message brokers, hiding the underlying complexity from the user. +This _does not_ mean it implements the full set of features of each of the messaging systems. In fact, it is expected +that most implementations will need to do work to adapt their system to this interface (e.g., in Kafka, you'd have +to mark the message as completed once the call to handle returns). -This standardization can benefit various scenarios, such as microservice architectures, where each microservice can communicate with other microservices using the messaging service interfaces. Similarly, applications that need to handle event-driven or streaming data can benefit from the push-based message delivery mechanism provided by the `consumer` and `handler` interfaces. Overall, the messaging service interfaces aim to make it easier to build complex and scalable software systems by providing a common foundation for message-based communication. +As mentioned above, this should still be completely compatible with any more advanced use cases of the various +message systems. For example, if you have a queue of work that is currently being handled by pre-existing software +outside of Wasm components, a component could use this interface to publish messages that get ingested into this queue. -### Non-goals +Another way to state the portability criteria is that this implementation should not break the possibility of a +component consuming this interface to be integrated with a more advanced messaging use case. -- The messaging service interfaces do not aim to provide advanced features of message brokers, such as broker clustering, message persistence, or guaranteed message delivery. These are implementation-specific details that are not addressed by the interfaces. -- The messaging service interfaces do not aim to provide support for every possible messaging pattern or use case. Instead, they focus on the common use cases of pub-sub and push-based message delivery. Other messaging patterns, such as request-reply or publish-confirm-subscribe, are outside the scope of the interfaces. -- The messaging service interfaces do not aim to provide a specific implementation of a message broker. Instead, they provide a standard way to interact with any message broker that supports the interfaces. +## Dev notes -### API walk-through +To regenerate the `.md` files, run: +```sh +wit-bindgen markdown ./wit/ -w imports --html-in-md +wit-bindgen markdown ./wit/ -w imports-request-reply --html-in-md +wit-bindgen markdown ./wit/ -w messaging-core --html-in-md +wit-bindgen markdown ./wit/ -w messaging-request-reply --html-in-md +``` -For a full API walk-through, see [wasi-messaging-demo](https://github.com/danbugs/wasi-messaging-demo). +It would make sense for a lot of these functions to be asynchronous, but that is not currently natively supported in +the component model. Asynchronous support will be added as part of WASI Preview 3. When async support becomes +available, we plan to update the wasi-messaging interface to incorporate asynchronous patterns. -> Note: This README needs to be expanded to cover a number of additional fields suggested in the -[WASI Proposal template](https://github.com/WebAssembly/wasi-proposal-template). \ No newline at end of file +> **Note**: Ensure you have version 0.34.0 of `wit-bindgen` installed to avoid compatibility issues. diff --git a/imports-request-reply.md b/imports-request-reply.md new file mode 100644 index 0000000..3d82a5d --- /dev/null +++ b/imports-request-reply.md @@ -0,0 +1,266 @@ +

World imports-request-reply

+

The imports-request-reply world extends imports by including the request-reply interface. +This allows the component to perform request/reply messaging patterns.

+ +

Import interface wasmcloud:messaging/types@0.3.0

+
+

Types

+

type metadata

+

metadata

+

A type alias for list> to represent metadata attached to a message +

type topic

+

string

+

A type alias for string to represent a message topic +

resource client

+

A connection to a message-exchange service (e.g., buffer, broker, etc.).

+

variant error

+

Errors that can occur when using the messaging interface.

+
Variant Cases
+ +

resource message

+

A message with a binary payload and additional information

+

Functions

+

[static]client.connect: func

+
Params
+ +
Return values
+ +

[method]client.disconnect: func

+
Params
+ +
Return values
+ +

[constructor]message: func

+
Params
+ +
Return values
+ +

[method]message.topic: func

+

The topic/subject/channel this message was received on

+
Params
+ +
Return values
+ +

[method]message.content-type: func

+

An optional content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +
Return values
+ +

[method]message.set-content-type: func

+

Set the content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +

[method]message.data: func

+

An opaque blob of data

+
Params
+ +
Return values
+ +

[method]message.set-data: func

+

Set the opaque blob of data for this message, discarding the old value

+
Params
+ +

[method]message.metadata: func

+

Optional metadata (also called headers or attributes in some systems) attached to the +message. This metadata is simply decoration and should not be interpreted by a host +to ensure portability across different implementors (e.g., Kafka -> NATS, etc.).

+
Params
+ +
Return values
+ +

[method]message.add-metadata: func

+

Add a new key-value pair to the metadata, overwriting any existing value for the same key

+
Params
+ +

[method]message.set-metadata: func

+

Set the metadata

+
Params
+ +

[method]message.remove-metadata: func

+

Remove a key-value pair from the metadata

+
Params
+ +

Import interface wasmcloud:messaging/request-reply@0.3.0

+

The request-reply interface allows a guest to send a message and await a response. This +interface is considered optional as not all message services support the concept of +request/reply. However, request/reply is a very common pattern in messaging and as such, we have +included it as a core interface.

+
+

Types

+

type client

+

client

+

+#### `type message` +[`message`](#message) +

+#### `type error` +[`error`](#error) +

+#### `resource request-options` +

Options for a request/reply operation. This is a resource to allow for future expansion of +options.

+

Functions

+

[constructor]request-options: func

+

Creates a new request options resource with no options set.

+
Return values
+ +

[method]request-options.set-timeout-ms: func

+

The maximum amount of time to wait for a response. If the timeout value is not set, then +the request/reply operation will block until a message is received in response.

+
Params
+ +

[method]request-options.set-expected-replies: func

+

The maximum number of replies to expect before returning.

+
Params
+ +

request: func

+

Performs a blocking request/reply operation with an optional set of request options.

+

The behavior of this function is largely dependent on the options given to the function. +If no options are provided, then the request/reply operation will block until a single +message is received in response. If a timeout is provided, then the request/reply operation +will block for the specified amount of time before returning an error if no messages were +received (or the list of messages that were received). If both a timeout and an expected +number of replies are provided, the function should return when either condition is met +(whichever comes first)—e.g., (1) if no replies were received within the timeout return an +error, (2) if the maximum expected number of replies were received before timeout, return +the list of messages, or (3) if the timeout is reached before the expected number of replies, +return the list of messages received up to that point.

+
Params
+ +
Return values
+ +

reply: func

+

Replies to the given message with the given response message. The details of which topic +the message is sent to is up to the implementation. This allows for reply-to details to be +handled in the best way possible for the underlying messaging system.

+

Please note that this reply functionality is different than something like HTTP because there +are several use cases in which a reply might not be required for every message (so this would +be a noop). There are also cases when you might want to reply and then continue processing. +Additionally, you might want to reply to a message several times (such as providing an +update). So this function is allowed to be called multiple times, unlike something like HTTP +where the reply is sent and the connection is closed.

+
Params
+ +
Return values
+ +

Import interface wasmcloud:messaging/producer@0.3.0

+

The producer interface is used to send messages to a channel/topic.

+
+

Types

+

type client

+

client

+

+#### `type message` +[`message`](#message) +

+#### `type error` +[`error`](#error) +

+#### `type topic` +[`topic`](#topic) +

+---- +

Functions

+

send: func

+

Sends the message using the given client.

+
Params
+ +
Return values
+ diff --git a/imports.md b/imports.md new file mode 100644 index 0000000..6b9748b --- /dev/null +++ b/imports.md @@ -0,0 +1,183 @@ +

World imports

+

The imports world defines the interfaces that the component will import from the host. +It includes the producer interface for sending messages.

+ +

Import interface wasmcloud:messaging/types@0.3.0

+
+

Types

+

type metadata

+

metadata

+

A type alias for list> to represent metadata attached to a message +

type topic

+

string

+

A type alias for string to represent a message topic +

resource client

+

A connection to a message-exchange service (e.g., buffer, broker, etc.).

+

variant error

+

Errors that can occur when using the messaging interface.

+
Variant Cases
+ +

resource message

+

A message with a binary payload and additional information

+

Functions

+

[static]client.connect: func

+
Params
+ +
Return values
+ +

[method]client.disconnect: func

+
Params
+ +
Return values
+ +

[constructor]message: func

+
Params
+ +
Return values
+ +

[method]message.topic: func

+

The topic/subject/channel this message was received on

+
Params
+ +
Return values
+ +

[method]message.content-type: func

+

An optional content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +
Return values
+ +

[method]message.set-content-type: func

+

Set the content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +

[method]message.data: func

+

An opaque blob of data

+
Params
+ +
Return values
+ +

[method]message.set-data: func

+

Set the opaque blob of data for this message, discarding the old value

+
Params
+ +

[method]message.metadata: func

+

Optional metadata (also called headers or attributes in some systems) attached to the +message. This metadata is simply decoration and should not be interpreted by a host +to ensure portability across different implementors (e.g., Kafka -> NATS, etc.).

+
Params
+ +
Return values
+ +

[method]message.add-metadata: func

+

Add a new key-value pair to the metadata, overwriting any existing value for the same key

+
Params
+ +

[method]message.set-metadata: func

+

Set the metadata

+
Params
+ +

[method]message.remove-metadata: func

+

Remove a key-value pair from the metadata

+
Params
+ +

Import interface wasmcloud:messaging/producer@0.3.0

+

The producer interface is used to send messages to a channel/topic.

+
+

Types

+

type client

+

client

+

+#### `type message` +[`message`](#message) +

+#### `type error` +[`error`](#error) +

+#### `type topic` +[`topic`](#topic) +

+---- +

Functions

+

send: func

+

Sends the message using the given client.

+
Params
+ +
Return values
+ diff --git a/messaging-core.md b/messaging-core.md new file mode 100644 index 0000000..d7737ea --- /dev/null +++ b/messaging-core.md @@ -0,0 +1,215 @@ +

World messaging-core

+

The messaging-core world includes the basic imports and exports the incoming-handler, +enabling the component to handle incoming messages without request/reply capabilities.

+ +

Import interface wasmcloud:messaging/types@0.3.0

+
+

Types

+

type metadata

+

metadata

+

A type alias for list> to represent metadata attached to a message +

type topic

+

string

+

A type alias for string to represent a message topic +

resource client

+

A connection to a message-exchange service (e.g., buffer, broker, etc.).

+

variant error

+

Errors that can occur when using the messaging interface.

+
Variant Cases
+ +

resource message

+

A message with a binary payload and additional information

+

Functions

+

[static]client.connect: func

+
Params
+ +
Return values
+ +

[method]client.disconnect: func

+
Params
+ +
Return values
+ +

[constructor]message: func

+
Params
+ +
Return values
+ +

[method]message.topic: func

+

The topic/subject/channel this message was received on

+
Params
+ +
Return values
+ +

[method]message.content-type: func

+

An optional content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +
Return values
+ +

[method]message.set-content-type: func

+

Set the content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +

[method]message.data: func

+

An opaque blob of data

+
Params
+ +
Return values
+ +

[method]message.set-data: func

+

Set the opaque blob of data for this message, discarding the old value

+
Params
+ +

[method]message.metadata: func

+

Optional metadata (also called headers or attributes in some systems) attached to the +message. This metadata is simply decoration and should not be interpreted by a host +to ensure portability across different implementors (e.g., Kafka -> NATS, etc.).

+
Params
+ +
Return values
+ +

[method]message.add-metadata: func

+

Add a new key-value pair to the metadata, overwriting any existing value for the same key

+
Params
+ +

[method]message.set-metadata: func

+

Set the metadata

+
Params
+ +

[method]message.remove-metadata: func

+

Remove a key-value pair from the metadata

+
Params
+ +

Import interface wasmcloud:messaging/producer@0.3.0

+

The producer interface is used to send messages to a channel/topic.

+
+

Types

+

type client

+

client

+

+#### `type message` +[`message`](#message) +

+#### `type error` +[`error`](#error) +

+#### `type topic` +[`topic`](#topic) +

+---- +

Functions

+

send: func

+

Sends the message using the given client.

+
Params
+ +
Return values
+ +

Export interface wasmcloud:messaging/incoming-handler@0.3.0

+
+

Types

+

type message

+

message

+

+#### `type error` +[`error`](#error) +

+#### `type topic` +[`topic`](#topic) +

+---- +

Functions

+

handle: func

+

Whenever this guest receives a message in one of the subscribed topics, the message is +sent to this handler. The guest is responsible for matching on the topic and handling the +message accordingly. Implementors (such as hosts) calling this interface should make their +own decisions on how to handle errors returned from this function.

+
Params
+ +
Return values
+ diff --git a/messaging-request-reply.md b/messaging-request-reply.md new file mode 100644 index 0000000..6f0e0cb --- /dev/null +++ b/messaging-request-reply.md @@ -0,0 +1,299 @@ +

World messaging-request-reply

+

The messaging-request-reply world combines imports-request-reply with the incoming-handler +export. This setup allows the host to interact with the component for both sending messages and +handling incoming messages with request/reply capabilities.

+ +

Import interface wasmcloud:messaging/types@0.3.0

+
+

Types

+

type metadata

+

metadata

+

A type alias for list> to represent metadata attached to a message +

type topic

+

string

+

A type alias for string to represent a message topic +

resource client

+

A connection to a message-exchange service (e.g., buffer, broker, etc.).

+

variant error

+

Errors that can occur when using the messaging interface.

+
Variant Cases
+ +

resource message

+

A message with a binary payload and additional information

+

Functions

+

[static]client.connect: func

+
Params
+ +
Return values
+ +

[method]client.disconnect: func

+
Params
+ +
Return values
+ +

[constructor]message: func

+
Params
+ +
Return values
+ +

[method]message.topic: func

+

The topic/subject/channel this message was received on

+
Params
+ +
Return values
+ +

[method]message.content-type: func

+

An optional content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +
Return values
+ +

[method]message.set-content-type: func

+

Set the content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +

[method]message.data: func

+

An opaque blob of data

+
Params
+ +
Return values
+ +

[method]message.set-data: func

+

Set the opaque blob of data for this message, discarding the old value

+
Params
+ +

[method]message.metadata: func

+

Optional metadata (also called headers or attributes in some systems) attached to the +message. This metadata is simply decoration and should not be interpreted by a host +to ensure portability across different implementors (e.g., Kafka -> NATS, etc.).

+
Params
+ +
Return values
+ +

[method]message.add-metadata: func

+

Add a new key-value pair to the metadata, overwriting any existing value for the same key

+
Params
+ +

[method]message.set-metadata: func

+

Set the metadata

+
Params
+ +

[method]message.remove-metadata: func

+

Remove a key-value pair from the metadata

+
Params
+ +

Import interface wasmcloud:messaging/request-reply@0.3.0

+

The request-reply interface allows a guest to send a message and await a response. This +interface is considered optional as not all message services support the concept of +request/reply. However, request/reply is a very common pattern in messaging and as such, we have +included it as a core interface.

+
+

Types

+

type client

+

client

+

+#### `type message` +[`message`](#message) +

+#### `type error` +[`error`](#error) +

+#### `resource request-options` +

Options for a request/reply operation. This is a resource to allow for future expansion of +options.

+

Functions

+

[constructor]request-options: func

+

Creates a new request options resource with no options set.

+
Return values
+ +

[method]request-options.set-timeout-ms: func

+

The maximum amount of time to wait for a response. If the timeout value is not set, then +the request/reply operation will block until a message is received in response.

+
Params
+ +

[method]request-options.set-expected-replies: func

+

The maximum number of replies to expect before returning.

+
Params
+ +

request: func

+

Performs a blocking request/reply operation with an optional set of request options.

+

The behavior of this function is largely dependent on the options given to the function. +If no options are provided, then the request/reply operation will block until a single +message is received in response. If a timeout is provided, then the request/reply operation +will block for the specified amount of time before returning an error if no messages were +received (or the list of messages that were received). If both a timeout and an expected +number of replies are provided, the function should return when either condition is met +(whichever comes first)—e.g., (1) if no replies were received within the timeout return an +error, (2) if the maximum expected number of replies were received before timeout, return +the list of messages, or (3) if the timeout is reached before the expected number of replies, +return the list of messages received up to that point.

+
Params
+ +
Return values
+ +

reply: func

+

Replies to the given message with the given response message. The details of which topic +the message is sent to is up to the implementation. This allows for reply-to details to be +handled in the best way possible for the underlying messaging system.

+

Please note that this reply functionality is different than something like HTTP because there +are several use cases in which a reply might not be required for every message (so this would +be a noop). There are also cases when you might want to reply and then continue processing. +Additionally, you might want to reply to a message several times (such as providing an +update). So this function is allowed to be called multiple times, unlike something like HTTP +where the reply is sent and the connection is closed.

+
Params
+ +
Return values
+ +

Import interface wasmcloud:messaging/producer@0.3.0

+

The producer interface is used to send messages to a channel/topic.

+
+

Types

+

type client

+

client

+

+#### `type message` +[`message`](#message) +

+#### `type error` +[`error`](#error) +

+#### `type topic` +[`topic`](#topic) +

+---- +

Functions

+

send: func

+

Sends the message using the given client.

+
Params
+ +
Return values
+ +

Export interface wasmcloud:messaging/incoming-handler@0.3.0

+
+

Types

+

type message

+

message

+

+#### `type error` +[`error`](#error) +

+#### `type topic` +[`topic`](#topic) +

+---- +

Functions

+

handle: func

+

Whenever this guest receives a message in one of the subscribed topics, the message is +sent to this handler. The guest is responsible for matching on the topic and handling the +message accordingly. Implementors (such as hosts) calling this interface should make their +own decisions on how to handle errors returned from this function.

+
Params
+ +
Return values
+ diff --git a/messaging.md b/messaging.md new file mode 100644 index 0000000..94e2842 --- /dev/null +++ b/messaging.md @@ -0,0 +1,188 @@ +

World messaging

+ +

Import interface wasi:messaging/types@0.2.0-draft

+
+

Types

+

resource client

+

A connection to a message-exchange service (e.g., buffer, broker, etc.).

+

variant error

+

Errors that can occur when using the messaging interface.

+
Variant Cases
+ +

resource message

+

A message with a binary payload and additional information

+

Functions

+

[static]client.connect: func

+
Params
+ +
Return values
+ +

[constructor]message: func

+
Params
+ +
Return values
+ +

[method]message.topic: func

+

The topic/subject/channel this message was received or should be sent on

+
Params
+ +
Return values
+ +

[method]message.set-topic: func

+

Set the topic/subject/channel this message should be sent on

+
Params
+ +

[method]message.content-type: func

+

An optional content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +
Return values
+ +

[method]message.set-content-type: func

+

Set the content-type describing the format of the data in the message. This is +sometimes described as the "format" type

+
Params
+ +

[method]message.data: func

+

An opaque blob of data

+
Params
+ +
Return values
+ +

[method]message.set-data: func

+

Set the opaque blob of data for this message, discarding the old value

+
Params
+ +

[method]message.metadata: func

+

Optional metadata (also called headers or attributes in some systems) attached to the +message

+
Params
+ +
Return values
+ +

[method]message.add-metadata: func

+

Add a new key-value pair to the metadata, overwriting any existing value for the same key

+
Params
+ +

Import interface wasi:messaging/producer@0.2.0-draft

+

The producer interface is used to send messages to a channel/topic.

+
+

Types

+

type client

+

client

+

+#### `type message` +[`message`](#message) +

+#### `type error` +[`error`](#error) +

+---- +

Functions

+

send: func

+

Sends the message using the given client.

+
Params
+ +
Return values
+ +

Export interface wasi:messaging/incoming-handler@0.2.0-draft

+
+

Types

+

type message

+

message

+

+#### `type error` +[`error`](#error) +

+---- +

Functions

+

handle: func

+

Whenever this guest receives a message in one of the subscribed channels, the message is +sent to this handler. The guest is responsible for matching on the channel and handling the +message accordingly. Implementors (such as hosts) calling this interface should make their +own decisions on how to handle errors returned from this function.

+
Params
+ +
Return values
+ +

subscribe: func

+

Subscribe to a list of topics (represented as strings) at runtime. +Implementors should consider also allowing subscriptions to be made at compile time via +some sort of configuration file.

+
Return values
+ diff --git a/wit/guest.wit b/wit/guest.wit new file mode 100644 index 0000000..73cfc72 --- /dev/null +++ b/wit/guest.wit @@ -0,0 +1,9 @@ +interface incoming-handler { + use types.{message, error, topic}; + + /// Whenever this guest receives a message in one of the subscribed topics, the message is + /// sent to this handler. The guest is responsible for matching on the topic and handling the + /// message accordingly. Implementors (such as hosts) calling this interface should make their + /// own decisions on how to handle errors returned from this function. + handle: func(message: message) -> result<_, error>; +} diff --git a/wit/messaging.wit b/wit/messaging.wit index 239d289..deca173 100644 --- a/wit/messaging.wit +++ b/wit/messaging.wit @@ -1,27 +1,29 @@ -package wasmcloud:messaging@0.2.0; +package wasmcloud:messaging@0.3.0; -// Types common to message broker interactions -interface types { - // A message sent to or received from a broker - record broker-message { - subject: string, - body: list, - reply-to: option, - } +/// The `imports` world defines the interfaces that the component will import from the host. +/// It includes the `producer` interface for sending messages. +world imports { + import producer; } -interface handler { - use types.{broker-message}; - - // Callback handled to invoke a function when a message is received from a subscription - handle-message: func(msg: broker-message) -> result<_, string>; +/// The `imports-request-reply` world extends `imports` by including the `request-reply` interface. +/// This allows the component to perform request/reply messaging patterns. +world imports-request-reply { + include imports; + import request-reply; } -interface consumer { - use types.{broker-message}; +/// The `messaging-request-reply` world combines `imports-request-reply` with the `incoming-handler` +/// export. This setup allows the host to interact with the component for both sending messages and +/// handling incoming messages with request/reply capabilities. +world messaging-request-reply { + include imports-request-reply; + export incoming-handler; +} - // Perform a request operation on a subject - request: func(subject: string, body: list, timeout-ms: u32) -> result; - // Publish a message to a subject without awaiting a response - publish: func(msg: broker-message) -> result<_, string>; +/// The `messaging-core` world includes the basic `imports` and exports the `incoming-handler`, +/// enabling the component to handle incoming messages without request/reply capabilities. +world messaging-core { + include imports; + export incoming-handler; } diff --git a/wit/producer.wit b/wit/producer.wit new file mode 100644 index 0000000..9caba60 --- /dev/null +++ b/wit/producer.wit @@ -0,0 +1,7 @@ +/// The producer interface is used to send messages to a channel/topic. +interface producer { + use types.{client, message, error, topic}; + + /// Sends the message using the given client. + send: func(c: borrow, topic: topic, message: message) -> result<_, error>; +} diff --git a/wit/request-reply.wit b/wit/request-reply.wit new file mode 100644 index 0000000..da8a543 --- /dev/null +++ b/wit/request-reply.wit @@ -0,0 +1,47 @@ +/// The request-reply interface allows a guest to send a message and await a response. This +/// interface is considered optional as not all message services support the concept of +/// request/reply. However, request/reply is a very common pattern in messaging and as such, we have +/// included it as a core interface. +interface request-reply { + use types.{client, message, error}; + + /// Options for a request/reply operation. This is a resource to allow for future expansion of + /// options. + resource request-options { + /// Creates a new request options resource with no options set. + constructor(); + + /// The maximum amount of time to wait for a response. If the timeout value is not set, then + /// the request/reply operation will block until a message is received in response. + set-timeout-ms: func(timeout-ms: u32); + + /// The maximum number of replies to expect before returning. + set-expected-replies: func(expected-replies: u32); + } + + /// Performs a blocking request/reply operation with an optional set of request options. + /// + /// The behavior of this function is largely dependent on the options given to the function. + /// If no options are provided, then the request/reply operation will block until a single + /// message is received in response. If a timeout is provided, then the request/reply operation + /// will block for the specified amount of time before returning an error if no messages were + /// received (or the list of messages that were received). If both a timeout and an expected + /// number of replies are provided, the function should return when either condition is met + /// (whichever comes first)—e.g., (1) if no replies were received within the timeout return an + /// error, (2) if the maximum expected number of replies were received before timeout, return + /// the list of messages, or (3) if the timeout is reached before the expected number of replies, + /// return the list of messages received up to that point. + request: func(c: borrow, message: borrow, options: option) -> result, error>; + + /// Replies to the given message with the given response message. The details of which topic + /// the message is sent to is up to the implementation. This allows for reply-to details to be + /// handled in the best way possible for the underlying messaging system. + /// + /// Please note that this reply functionality is different than something like HTTP because there + /// are several use cases in which a reply might not be required for every message (so this would + /// be a noop). There are also cases when you might want to reply and then continue processing. + /// Additionally, you might want to reply to a message several times (such as providing an + /// update). So this function is allowed to be called multiple times, unlike something like HTTP + /// where the reply is sent and the connection is closed. + reply: func(reply-to: borrow, message: message) -> result<_, error>; +} diff --git a/wit/types.wit b/wit/types.wit new file mode 100644 index 0000000..d7e8292 --- /dev/null +++ b/wit/types.wit @@ -0,0 +1,52 @@ +interface types { + /// A type alias for list> to represent metadata attached to a message + type metadata = list>; + + /// A type alias for string to represent a message topic + type topic = string; + + /// A connection to a message-exchange service (e.g., buffer, broker, etc.). + resource client { + connect: static func(name: string) -> result; + disconnect: func() -> result<_, error>; + } + + /// Errors that can occur when using the messaging interface. + variant error { + /// The request or operation timed out. + timeout, + /// An error occurred with the connection. Includes a message for additional context + connection(string), + /// A permission error occurred. Includes a message for additional context + permission-denied(string), + /// A catch all for other types of errors + other(string), + } + + /// A message with a binary payload and additional information + resource message { + constructor(data: list); + /// The topic/subject/channel this message was received on + topic: func() -> topic; + /// An optional content-type describing the format of the data in the message. This is + /// sometimes described as the "format" type + content-type: func() -> option; + /// Set the content-type describing the format of the data in the message. This is + /// sometimes described as the "format" type + set-content-type: func(content-type: string); + /// An opaque blob of data + data: func() -> list; + /// Set the opaque blob of data for this message, discarding the old value + set-data: func(data: list); + /// Optional metadata (also called headers or attributes in some systems) attached to the + /// message. This metadata is simply decoration and should not be interpreted by a host + /// to ensure portability across different implementors (e.g., Kafka -> NATS, etc.). + metadata: func() -> option; + /// Add a new key-value pair to the metadata, overwriting any existing value for the same key + add-metadata: func(key: string, value: string); + /// Set the metadata + set-metadata: func(meta: metadata); + /// Remove a key-value pair from the metadata + remove-metadata: func(key: string); + } +}