From 6231f8a10b24dc54d1fdc1387f25e2dd6f0c7f41 Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Mon, 26 Feb 2024 16:07:37 -0700 Subject: [PATCH 1/7] feat(*): Updates messaging to support request-reply This makes several updates to the messaging interface. Initially the README said that this wasn't going to support request/reply, but based on my reading of the Kafka, NATS, MQTT, and SQS APIs, this is a fairly common pattern. Another piece of evidence here is what I've seen as a wasmCloud maintainer from our users. Request/reply is one of the more common things we see with a messaging service. Please note that this doesn't _require_ the use of a reply-to topic, just exposes it for use. I also did a few other changes here. First is that I added the topic to the message. This was common across all systems and is often used by code to select the appropriate logic to perform. I also removed the format field as this didn't seem to be a common parameter across various services. We could definitely add a content-type member to this record in the future if needed, but I think much of that can be passed via the metadata field. There are other things I might suggest some changes to, but I want to think on them some more and open some issues to discuss them first Signed-off-by: Taylor Thomas --- README.md | 2 +- wit/producer.wit | 2 ++ wit/types.wit | 21 +++++++-------------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 414f56c..d3f2d7c 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Overall, the messaging interfaces aim to make it easier to build complex and sca ### Non-goals - 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 support for every possible messaging pattern or use case. Instead, they focus on the common use cases of pub-sub, push-based message delivery, and request-reply. Other messaging patterns, such as 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. ### API walk-through diff --git a/wit/producer.wit b/wit/producer.wit index fa795d8..8c08156 100644 --- a/wit/producer.wit +++ b/wit/producer.wit @@ -1,5 +1,7 @@ interface producer { use messaging-types.{client, channel, message, error}; + /// Sends a message to the given channel/topic. This topic can be overridden if a message has a + /// non-empty topic field send: func(c: client, ch: channel, m: list) -> result<_, error>; } diff --git a/wit/types.wit b/wit/types.wit index b7c9803..81e2e29 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -23,22 +23,15 @@ interface messaging-types { extensions: option>> } - /// Format specification for messages - /// - more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats - /// - message metadata can further decorate w/ things like format version, and so on. - enum format-spec { - cloudevents, - http, - amqp, - mqtt, - kafka, - raw - } - - /// A message with a binary payload, a format specification, and decorative metadata. + /// A message with a binary payload and additional information record message { + /// The topic or subject this message was received or should be sent on + topic: string, + /// An optional topic for use in request/response scenarios + reply-to: option, + /// An opaque blob of data data: list, - format: format-spec, + /// Optional metadata (also called headers or attributes in some systems) attached to the message metadata: option>> } } \ No newline at end of file From ba280477a479f87d3a01bfbcf7b9f1a3599c86df Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Mon, 13 May 2024 14:22:21 -0600 Subject: [PATCH 2/7] feat(*): Updates interfaces to be more streamlined This PR integrates various changes from talking to current users of messaging in the community as well as conversations among the champions Signed-off-by: Taylor Thomas --- imports-request-reply.md | 233 +++++++++++++++++++++++++++++++++++++++ imports.md | 127 +++++++++++---------- messaging.md | 143 ++++++++++++------------ wit/consumer.wit | 18 +-- wit/guest.wit | 11 +- wit/messaging.wit | 7 +- wit/producer.wit | 7 +- wit/request-reply.wit | 18 +++ wit/types.wit | 36 ++++-- 9 files changed, 444 insertions(+), 156 deletions(-) create mode 100644 imports-request-reply.md create mode 100644 wit/request-reply.wit diff --git a/imports-request-reply.md b/imports-request-reply.md new file mode 100644 index 0000000..bc3be87 --- /dev/null +++ b/imports-request-reply.md @@ -0,0 +1,233 @@ +

World imports-request-reply

+ +

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
+
    +
  • +

    unauthorized

    +

    The requested option is not authorized. This could be a topic it doesn't have +permission to subscribe to, or a permission it doesn't have to perform a specific +action. This error is mainly used when calling `update-guest-configuration`. +

  • +
  • +

    timeout

    +

    The request or operation timed out. +

  • +
  • +

    connection: string

    +

    An error occurred with the connection. Includes a message for additional context +

  • +
  • +

    other: string

    +

    A catch all for other types of errors +

  • +
+

type channel

+

string

+

There are two types of channels: +- publish-subscribe channel, which is a broadcast channel, and +- point-to-point channel, which is a unicast channel. +

The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

+

record guest-configuration

+

Configuration includes a required list of channels the guest is subscribing to, and an +optional list of extensions key-value pairs (e.g., partitions/offsets to read from in +Kafka/EventHubs, QoS etc.).

+
Record Fields
+
    +
  • channels: list<channel>
  • +
  • extensions: option<list<(string, string)>>
  • +
+

record message

+

A message with a binary payload and additional information

+
Record Fields
+
    +
  • +

    topic: channel

    +

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

  • +
  • +

    content-type: option<string>

    +

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

  • +
  • +

    reply-to: option<string>

    +

    An optional topic for use in request/response scenarios. Senders and consumers of +messages must not assume that this field is set and should handle it in their code +accordingly. +

  • +
  • +

    data: list<u8>

    +

    An opaque blob of data +

  • +
  • +

    metadata: option<list<(string, string)>>

    +

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

  • +
+
+

Functions

+

[static]client.connect: func

+
Params
+
    +
  • name: string
  • +
+
Return values
+ +

Import interface wasi:messaging/request-reply@0.2.0-draft

+

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) +

+---- +

Functions

+

request: func

+

Performs a blocking request/reply operation with an optional timeout. If the timeout value +is not set, then the request/reply operation will block indefinitely.

+

Please note that implementations that provide wasi:messaging are responsible for ensuring +that guests are not allowed to subscribe to channels that they are not configured to +subscribe to (or have access to). Failure to do so can result in possible breakout or access +to resources that are not intended to be accessible to the guest. This means implementations +should validate that the reply-to field is a valid topic the guest should have access to or +enforce it via the credentials used to connect to the service.

+
Params
+ +
Return values
+ +

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 channel` +[`channel`](#channel) +

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

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

+---- +

Functions

+

send: func

+

Sends a message to the given channel/topic. If the channel/topic is not empty, it will +override the channel/topic in the message.

+
Params
+ +
Return values
+ +

Import interface wasi:messaging/consumer@0.2.0-draft

+

The consumer interface allows a guest to dynamically update its subscriptions and configuration +as well as functionality for completing (acking) or abandoning (nacking) messages.

+
+

Types

+

type client

+

client

+

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

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

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

+#### `type guest-configuration` +[`guest-configuration`](#guest_configuration) +

+---- +

Functions

+

update-guest-configuration: func

+

'Fit-all' type function for updating a guest's configuration – this could be useful for:

+
    +
  • unsubscribing from a channel,
  • +
  • checkpointing,
  • +
  • etc..
  • +
+

Please note that implementations that provide wasi:messaging are responsible for ensuring +that guests are not allowed to subscribe to channels that they are not configured to +subscribe to (or have access to). Failure to do so can result in possible breakout or access +to resources that are not intended to be accessible to the guest. This means implementations +should validate that the configured topics are valid topics the guest should have access to or +enforce it via the credentials used to connect to the service.

+
Params
+ +
Return values
+ +

complete-message: func

+

A message can exist under several statuses: +(1) available: the message is ready to be read, +(2) acquired: the message has been sent to a consumer (but still exists in the queue), +(3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, +(4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:

+
    +
  • deleted,
  • +
  • sent to a dead-letter queue, or
  • +
  • kept in the queue for further processing.
  • +
+
Params
+ +
Return values
+ +

abandon-message: func

+
Params
+ +
Return values
+ diff --git a/imports.md b/imports.md index cc4b825..1ef5346 100644 --- a/imports.md +++ b/imports.md @@ -2,19 +2,40 @@ -

Import interface wasi:messaging/messaging-types@0.2.0-draft

+

Import interface wasi:messaging/types@0.2.0-draft


Types

resource client

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

-

resource error

-

TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.

+

variant error

+

Errors that can occur when using the messaging interface.

+
Variant Cases
+
    +
  • +

    unauthorized

    +

    The requested option is not authorized. This could be a topic it doesn't have +permission to subscribe to, or a permission it doesn't have to perform a specific +action. This error is mainly used when calling `update-guest-configuration`. +

  • +
  • +

    timeout

    +

    The request or operation timed out. +

  • +
  • +

    connection: string

    +

    An error occurred with the connection. Includes a message for additional context +

  • +
  • +

    other: string

    +

    A catch all for other types of errors +

  • +

type channel

string

There are two types of channels: @@ -22,35 +43,42 @@ - point-to-point channel, which is a unicast channel.

The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

record guest-configuration

-

Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs -(e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).

+

Configuration includes a required list of channels the guest is subscribing to, and an +optional list of extensions key-value pairs (e.g., partitions/offsets to read from in +Kafka/EventHubs, QoS etc.).

Record Fields
  • channels: list<channel>
  • extensions: option<list<(string, string)>>
-

enum format-spec

-

Format specification for messages

-
    -
  • more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats
  • -
  • message metadata can further decorate w/ things like format version, and so on.
  • -
-
Enum Cases
-
    -
  • cloudevents
  • -
  • http
  • -
  • amqp
  • -
  • mqtt
  • -
  • kafka
  • -
  • raw
  • -

record message

-

A message with a binary payload, a format specification, and decorative metadata.

+

A message with a binary payload and additional information

Record Fields
    -
  • data: list<u8>
  • -
  • format: format-spec
  • -
  • metadata: option<list<(string, string)>>
  • +
  • +

    topic: channel

    +

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

  • +
  • +

    content-type: option<string>

    +

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

  • +
  • +

    reply-to: option<string>

    +

    An optional topic for use in request/response scenarios. Senders and consumers of +messages must not assume that this field is set and should handle it in their code +accordingly. +

  • +
  • +

    data: list<u8>

    +

    An opaque blob of data +

  • +
  • +

    metadata: option<list<(string, string)>>

    +

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


Functions

@@ -61,14 +89,10 @@
Return values
-

[static]error.trace: func

-
Return values
-

Import interface wasi:messaging/producer@0.2.0-draft

+

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


Types

type client

@@ -86,6 +110,8 @@ ----

Functions

send: func

+

Sends a message to the given channel/topic. If the channel/topic is not empty, it will +override the channel/topic in the message.

Params
  • c: own<client>
  • @@ -94,9 +120,11 @@
Return values

Import interface wasi:messaging/consumer@0.2.0-draft

+

The consumer interface allows a guest to dynamically update its subscriptions and configuration +as well as functionality for completing (acking) or abandoning (nacking) messages.


Types

type client

@@ -116,29 +144,6 @@

----

Functions

-

subscribe-try-receive: func

-

Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None

-
Params
- -
Return values
- -

subscribe-receive: func

-

Blocking receive until message with ephemeral subscription

-
Params
- -
Return values
-

update-guest-configuration: func

'Fit-all' type function for updating a guest's configuration – this could be useful for:

    @@ -146,13 +151,19 @@
  • checkpointing,
  • etc..
+

Please note that implementations that provide wasi:messaging are responsible for ensuring +that guests are not allowed to subscribe to channels that they are not configured to +subscribe to (or have access to). Failure to do so can result in possible breakout or access +to resources that are not intended to be accessible to the guest. This means implementations +should validate that the configured topics are valid topics the guest should have access to or +enforce it via the credentials used to connect to the service.

Params
Return values

complete-message: func

A message can exist under several statuses: @@ -171,7 +182,7 @@

Return values

abandon-message: func

Params
@@ -180,5 +191,5 @@
Return values
diff --git a/messaging.md b/messaging.md index 3774be8..9765a8e 100644 --- a/messaging.md +++ b/messaging.md @@ -2,24 +2,45 @@ -

Import interface wasi:messaging/messaging-types@0.2.0-draft

+

Import interface wasi:messaging/types@0.2.0-draft


Types

resource client

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

-

resource error

-

TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.

+

variant error

+

Errors that can occur when using the messaging interface.

+
Variant Cases
+
    +
  • +

    unauthorized

    +

    The requested option is not authorized. This could be a topic it doesn't have +permission to subscribe to, or a permission it doesn't have to perform a specific +action. This error is mainly used when calling `update-guest-configuration`. +

  • +
  • +

    timeout

    +

    The request or operation timed out. +

  • +
  • +

    connection: string

    +

    An error occurred with the connection. Includes a message for additional context +

  • +
  • +

    other: string

    +

    A catch all for other types of errors +

  • +

type channel

string

There are two types of channels: @@ -27,35 +48,42 @@ - point-to-point channel, which is a unicast channel.

The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

record guest-configuration

-

Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs -(e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).

+

Configuration includes a required list of channels the guest is subscribing to, and an +optional list of extensions key-value pairs (e.g., partitions/offsets to read from in +Kafka/EventHubs, QoS etc.).

Record Fields
  • channels: list<channel>
  • extensions: option<list<(string, string)>>
-

enum format-spec

-

Format specification for messages

-
    -
  • more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats
  • -
  • message metadata can further decorate w/ things like format version, and so on.
  • -
-
Enum Cases
-
    -
  • cloudevents
  • -
  • http
  • -
  • amqp
  • -
  • mqtt
  • -
  • kafka
  • -
  • raw
  • -

record message

-

A message with a binary payload, a format specification, and decorative metadata.

+

A message with a binary payload and additional information

Record Fields
    -
  • data: list<u8>
  • -
  • format: format-spec
  • -
  • metadata: option<list<(string, string)>>
  • +
  • +

    topic: channel

    +

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

  • +
  • +

    content-type: option<string>

    +

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

  • +
  • +

    reply-to: option<string>

    +

    An optional topic for use in request/response scenarios. Senders and consumers of +messages must not assume that this field is set and should handle it in their code +accordingly. +

  • +
  • +

    data: list<u8>

    +

    An opaque blob of data +

  • +
  • +

    metadata: option<list<(string, string)>>

    +

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


Functions

@@ -66,14 +94,10 @@
Return values
-

[static]error.trace: func

-
Return values
-

Import interface wasi:messaging/producer@0.2.0-draft

+

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


Types

type client

@@ -91,6 +115,8 @@ ----

Functions

send: func

+

Sends a message to the given channel/topic. If the channel/topic is not empty, it will +override the channel/topic in the message.

Params
  • c: own<client>
  • @@ -99,9 +125,11 @@
Return values

Import interface wasi:messaging/consumer@0.2.0-draft

+

The consumer interface allows a guest to dynamically update its subscriptions and configuration +as well as functionality for completing (acking) or abandoning (nacking) messages.


Types

type client

@@ -121,29 +149,6 @@

----

Functions

-

subscribe-try-receive: func

-

Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None

-
Params
- -
Return values
- -

subscribe-receive: func

-

Blocking receive until message with ephemeral subscription

-
Params
- -
Return values
-

update-guest-configuration: func

'Fit-all' type function for updating a guest's configuration – this could be useful for:

    @@ -151,13 +156,19 @@
  • checkpointing,
  • etc..
+

Please note that implementations that provide wasi:messaging are responsible for ensuring +that guests are not allowed to subscribe to channels that they are not configured to +subscribe to (or have access to). Failure to do so can result in possible breakout or access +to resources that are not intended to be accessible to the guest. This means implementations +should validate that the configured topics are valid topics the guest should have access to or +enforce it via the credentials used to connect to the service.

Params
Return values

complete-message: func

A message can exist under several statuses: @@ -176,7 +187,7 @@

Return values

abandon-message: func

Params
@@ -185,9 +196,9 @@
Return values
-

Export interface wasi:messaging/messaging-guest@0.2.0-draft

+

Export interface wasi:messaging/guest@0.2.0-draft


Types

type message

@@ -201,20 +212,14 @@

----

Functions

-

configure: func

-

Returns the list of channels (and extension metadata within guest-configuration) that -this component should subscribe to and be handled by the subsequent handler within guest-configuration

-
Return values
-

handler: func

-

Whenever this guest receives a message in one of the subscribed channels, the message is sent to this handler

+

Whenever this guest receives a message in one of the subscribed channels, the message is +sent to this handler

Params
Return values
diff --git a/wit/consumer.wit b/wit/consumer.wit index a8f14d5..76c105a 100644 --- a/wit/consumer.wit +++ b/wit/consumer.wit @@ -1,17 +1,19 @@ +/// The consumer interface allows a guest to dynamically update its subscriptions and configuration +/// as well as functionality for completing (acking) or abandoning (nacking) messages. interface consumer { - // {client, message, channel, error, guest-configuration} - use messaging-types.{client, message, channel, error, guest-configuration}; - - /// Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None - subscribe-try-receive: func(c: client, ch: channel, t-milliseconds: u32) -> result>, error>; - - /// Blocking receive until message with ephemeral subscription - subscribe-receive: func(c: client, ch: channel) -> result, error>; + use types.{client, message, channel, error, guest-configuration}; /// 'Fit-all' type function for updating a guest's configuration – this could be useful for: /// - unsubscribing from a channel, /// - checkpointing, /// - etc.. + /// + /// Please note that implementations that provide `wasi:messaging` are responsible for ensuring + /// that guests are not allowed to subscribe to channels that they are not configured to + /// subscribe to (or have access to). Failure to do so can result in possible breakout or access + /// to resources that are not intended to be accessible to the guest. This means implementations + /// should validate that the configured topics are valid topics the guest should have access to or + /// enforce it via the credentials used to connect to the service. update-guest-configuration: func(gc: guest-configuration) -> result<_, error>; /// A message can exist under several statuses: diff --git a/wit/guest.wit b/wit/guest.wit index 8e9cb4e..9138149 100644 --- a/wit/guest.wit +++ b/wit/guest.wit @@ -1,10 +1,7 @@ -interface messaging-guest { - use messaging-types.{message, guest-configuration, error}; +interface guest { + use types.{message, guest-configuration, error}; - /// Returns the list of channels (and extension metadata within guest-configuration) that - /// this component should subscribe to and be handled by the subsequent handler within guest-configuration - configure: func() -> result; - - /// Whenever this guest receives a message in one of the subscribed channels, the message is sent to this handler + /// Whenever this guest receives a message in one of the subscribed channels, the message is + /// sent to this handler handler: func(ms: list) -> result<_, error>; } diff --git a/wit/messaging.wit b/wit/messaging.wit index 26ad817..2d18024 100644 --- a/wit/messaging.wit +++ b/wit/messaging.wit @@ -5,7 +5,12 @@ world imports { import consumer; } +world imports-request-reply { + include imports; + import request-reply; +} + world messaging { include imports; - export messaging-guest; + export guest; } \ No newline at end of file diff --git a/wit/producer.wit b/wit/producer.wit index 8c08156..99931d3 100644 --- a/wit/producer.wit +++ b/wit/producer.wit @@ -1,7 +1,8 @@ +/// The producer interface is used to send messages to a channel/topic. interface producer { - use messaging-types.{client, channel, message, error}; + use types.{client, channel, message, error}; - /// Sends a message to the given channel/topic. This topic can be overridden if a message has a - /// non-empty topic field + /// Sends a message to the given channel/topic. If the channel/topic is not empty, it will + /// override the channel/topic in the message. send: func(c: client, ch: channel, m: list) -> result<_, error>; } diff --git a/wit/request-reply.wit b/wit/request-reply.wit new file mode 100644 index 0000000..bf08022 --- /dev/null +++ b/wit/request-reply.wit @@ -0,0 +1,18 @@ +/// 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}; + + /// Performs a blocking request/reply operation with an optional timeout. If the timeout value + /// is not set, then the request/reply operation will block indefinitely. + /// + /// Please note that implementations that provide `wasi:messaging` are responsible for ensuring + /// that guests are not allowed to subscribe to channels that they are not configured to + /// subscribe to (or have access to). Failure to do so can result in possible breakout or access + /// to resources that are not intended to be accessible to the guest. This means implementations + /// should validate that the reply-to field is a valid topic the guest should have access to or + /// enforce it via the credentials used to connect to the service. + request: func(c: client, msg: message, timeout-ms: option) -> result>, error>; +} \ No newline at end of file diff --git a/wit/types.wit b/wit/types.wit index 81e2e29..0af0012 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -1,12 +1,21 @@ -interface messaging-types { +interface types { /// A connection to a message-exchange service (e.g., buffer, broker, etc.). resource client { connect: static func(name: string) -> result; } - /// TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces. - resource error { - trace: static func() -> string; + /// Errors that can occur when using the messaging interface. + variant error { + /// The requested option is not authorized. This could be a topic it doesn't have + /// permission to subscribe to, or a permission it doesn't have to perform a specific + /// action. This error is mainly used when calling `update-guest-configuration`. + unauthorized, + /// The request or operation timed out. + timeout, + /// An error occurred with the connection. Includes a message for additional context + connection(string), + /// A catch all for other types of errors + other(string), } /// There are two types of channels: @@ -16,8 +25,9 @@ interface messaging-types { /// The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue. type channel = string; - /// Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs - /// (e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.). + /// Configuration includes a required list of channels the guest is subscribing to, and an + /// optional list of extensions key-value pairs (e.g., partitions/offsets to read from in + /// Kafka/EventHubs, QoS etc.). record guest-configuration { channels: list, extensions: option>> @@ -25,13 +35,19 @@ interface messaging-types { /// A message with a binary payload and additional information record message { - /// The topic or subject this message was received or should be sent on - topic: string, - /// An optional topic for use in request/response scenarios + /// The topic/subject/channel this message was received or should be sent on + topic: channel, + /// An optional content-type describing the format of the data in the message. This is + /// sometimes described as the "format" type + content-type: option, + /// An optional topic for use in request/response scenarios. Senders and consumers of + /// messages must not assume that this field is set and should handle it in their code + /// accordingly. reply-to: option, /// An opaque blob of data data: list, - /// Optional metadata (also called headers or attributes in some systems) attached to the message + /// Optional metadata (also called headers or attributes in some systems) attached to the + /// message metadata: option>> } } \ No newline at end of file From 18318a518b15a0eb02c5c30f49715e7e30283258 Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Fri, 31 May 2024 15:19:51 -0600 Subject: [PATCH 3/7] feat(*)!: Additional changes based on PR feedback I also deleted the examples.md for now until we settle on the interface. It will be easier to add back in once we have some real world examples to point at Signed-off-by: Taylor Thomas --- examples.md | 178 ----------------------------------- imports-request-reply.md | 195 +++++++++++++++++++++++---------------- imports.md | 172 +++++++++++++++++++--------------- messaging.md | 177 ++++++++++++++++++++--------------- wit/consumer.wit | 14 +-- wit/guest.wit | 5 +- wit/producer.wit | 2 +- wit/request-reply.wit | 12 +-- wit/types.wit | 39 ++++++-- 9 files changed, 358 insertions(+), 436 deletions(-) delete mode 100644 examples.md diff --git a/examples.md b/examples.md deleted file mode 100644 index e11bf3e..0000000 --- a/examples.md +++ /dev/null @@ -1,178 +0,0 @@ -# Examples - -## Example 1: Guest Skeleton Usage - -The guest will implement the methods in [`wit/guest.wit`](/wit/guest.wit): -- `configure()`, which will be called by the host to setup long-lived subscriptions, and receive any other implementor specific extensions/config.. -- `handler()`, which will be called by the host to handle messages sent to the channels subscribed to in `configure()`. - -As Wasm modules/components are mostly intended to be short-lived, the host is meant to create new instances for configuration and each message handling. - -### Rust - -```rust -use crate::messaging_types::{GuestConfigurationResult, MessageResult}; - -wit_bindgen::generate!({ - path: "../wit", -}); - -struct MyGuest; - -impl guest::Guest for MyGuest { - fn configure() -> Result { - // Configure the messaging system - } - - fn handler(_ms: Vec) -> Result<(), u32> { - // Handle the message - } -} - -export_messaging!(MyGuest); -``` - -### C/C++ - -```c -#include "messaging.c" - -messaging_result_guest_configuration_error_t messaging_configure(void) { - // Configure the messaging system -} - -messaging_result_void_error_t messaging_handle(messaging_list_message_t *message) { - // Handle the message -} -``` - -### Go - -```go -package main - -import ( - msging "app/gen" -) - -func init() { - a := MsgingImpl{} - msging.SetExportsWasiMessagingMessagingGuest(a) -} - -type MsgingImpl struct { -} - -func (e MessagingImpl) Configure msging.Result[msging.WasiMessagingConsumerGuestConfiguration, msging.WasiMessagingMessagingGuestError] { - // Configure the messaging system -} - -func (e MessagingImpl) Handler msging.Result[struct{}, msging.WasiMessagingMessagingGuestError] { - // Handle the message -} - -func main() {} -``` - -## Example 2: Guest Usage - -### Rust - -```rust -use crate::messaging_types::{GuestConfigurationResult, MessageResult, connect, disconnect}; - -wit_bindgen::generate!({ - path: "../wit", -}); - -struct MyGuest; - -impl guest::Guest for MyGuest { - fn configure() -> Result { - // This function will be called by the host, who will be maintaining a - // long-lived client connection to a broker or other messaging system. - // The client will be subscribed to channels a, b, and c) with no extra configuration. - // As soon as configuration is set, the host should kill the Wasm instance. - Ok(GuestConfigurationResult { - channels: vec!["a".to_string(), "b".to_string(), "c".to_string()], - ..Default::default() - }) - } - - fn handler(ms: Vec) -> Result<(), u32> { - // Whenever a message is received on a subscribed channel (from configure()), - // the host will call this function. Once the message has been handled, - // the host is expected to kill the Wasm instance. - - for m in ms { - - // match on message metadata for channel name - match m.metadata { - Some(metadata) => { - for (k, v) in metadata { - if k == "channel" { - match v.as_str() { - "a" => { - // handle message from channel a - // [...] - - // unsubscribe from channel a - update_guest_configuration(GuestConfigurationResult { - channels: vec!["b".to_string(), "c".to_string()], - ..Default::default() - }).unwrap() - - // abandon message - consumer::abandon_message(m).unwrap(); - } - "b" => { - // handle message from channel b - // [...] - - // request-reply from channel d - let client = connect("some-broker").unwrap(); - let msgs = subscribe_try_receive(client, "d", 100).unwrap(); - - // do something with msgs - // [...] - - // disconnect client - disconnect(client); - - // complete message - consumer::complete_message(m).unwrap(); - } - "c" => { - // handle message from channel c - // [...] - - // send message to channel d - let client = connect("some-broker").unwrap(); - let message = MessageParam { - data: "hello from guest".as_bytes(), - format: messaging_types::FormatSpec::Raw, - metadata: None, - }; - producer::send(client, "d", &[message]).unwrap(); - disconnect(client); - - // complete message - consumer::complete_message(m).unwrap(); - } - _ => { - // handle message from unknown channel - } - } - } - } - } - None => { - // handle message with no metadata - } - } - } - } -} - -export_messaging!(MyGuest); -``` \ No newline at end of file diff --git a/imports-request-reply.md b/imports-request-reply.md index bc3be87..d183745 100644 --- a/imports-request-reply.md +++ b/imports-request-reply.md @@ -52,36 +52,8 @@ Kafka/EventHubs, QoS etc.).

  • channels: list<channel>
  • extensions: option<list<(string, string)>>
  • -

    record message

    -

    A message with a binary payload and additional information

    -
    Record Fields
    -
      -
    • -

      topic: channel

      -

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

    • -
    • -

      content-type: option<string>

      -

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

    • -
    • -

      reply-to: option<string>

      -

      An optional topic for use in request/response scenarios. Senders and consumers of -messages must not assume that this field is set and should handle it in their code -accordingly. -

    • -
    • -

      data: list<u8>

      -

      An opaque blob of data -

    • -
    • -

      metadata: option<list<(string, string)>>

      -

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

    • -
    -
    +

    resource message

    +

    A message with a binary payload and additional information

    Functions

    [static]client.connect: func

    Params
    @@ -92,6 +64,100 @@ message +

    [constructor]message: func

    +
    Params
    +
      +
    • topic: channel
    • +
    • data: list<u8>
    • +
    • content-type: option<string>
    • +
    • metadata: option<list<(string, string)>>
    • +
    +
    Return values
    + +

    [method]message.topic: func

    +

    The topic/subject/channel this message was received or should be sent 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
    +
      +
    • option<string>
    • +
    +

    [method]message.data: func

    +

    An opaque blob of data

    +
    Params
    + +
    Return values
    +
      +
    • list<u8>
    • +
    +

    [method]message.metadata: func

    +

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

    +
    Params
    + +
    Return values
    +
      +
    • option<list<(string, string)>>
    • +
    +

    [method]message.complete: func

    +

    Completes/acks the message

    +

    A message can exist under several statuses: +(1) available: the message is ready to be read, +(2) acquired: the message has been sent to a consumer (but still exists in the queue), +(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, +(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    +
      +
    • deleted,
    • +
    • sent to a dead-letter queue, or
    • +
    • kept in the queue for further processing.
    • +
    +
    Params
    + +
    Return values
    + +

    [method]message.abandon: func

    +

    Abandon/nacks the message

    +

    A message can exist under several statuses: +(1) available: the message is ready to be read, +(2) acquired: the message has been sent to a consumer (but still exists in the queue), +(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, +(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    +
      +
    • deleted,
    • +
    • sent to a dead-letter queue, or
    • +
    • kept in the queue for further processing.
    • +
    +
    Params
    + +
    Return values
    +

    Import interface wasi:messaging/request-reply@0.2.0-draft

    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 @@ -113,21 +179,28 @@ included it as a core interface.

    request: func

    Performs a blocking request/reply operation with an optional timeout. If the timeout value is not set, then the request/reply operation will block indefinitely.

    -

    Please note that implementations that provide wasi:messaging are responsible for ensuring -that guests are not allowed to subscribe to channels that they are not configured to -subscribe to (or have access to). Failure to do so can result in possible breakout or access -to resources that are not intended to be accessible to the guest. This means implementations -should validate that the reply-to field is a valid topic the guest should have access to or -enforce it via the credentials used to connect to the service.

    Params
    Return values
    +

    reply: func

    +

    Replies to the given message with the given response message. The details of which channel +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.

    +
    Params
    + +
    Return values
    +

    Import interface wasi:messaging/producer@0.2.0-draft

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

    @@ -154,28 +227,18 @@ override the channel/topic in the message.

    Return values

    Import interface wasi:messaging/consumer@0.2.0-draft

    -

    The consumer interface allows a guest to dynamically update its subscriptions and configuration -as well as functionality for completing (acking) or abandoning (nacking) messages.

    +

    The consumer interface allows a guest to dynamically update its subscriptions and configuration


    Types

    -

    type client

    -

    client

    -

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

    -#### `type channel` -[`channel`](#channel) -

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

    type error

    +

    error

    #### `type guest-configuration` [`guest-configuration`](#guest_configuration) @@ -203,31 +266,3 @@ enforce it via the credentials used to connect to the service.

    -

    complete-message: func

    -

    A message can exist under several statuses: -(1) available: the message is ready to be read, -(2) acquired: the message has been sent to a consumer (but still exists in the queue), -(3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, -(4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:

    -
      -
    • deleted,
    • -
    • sent to a dead-letter queue, or
    • -
    • kept in the queue for further processing.
    • -
    -
    Params
    - -
    Return values
    - -

    abandon-message: func

    -
    Params
    - -
    Return values
    - diff --git a/imports.md b/imports.md index 1ef5346..384da07 100644 --- a/imports.md +++ b/imports.md @@ -51,36 +51,8 @@ Kafka/EventHubs, QoS etc.).

  • channels: list<channel>
  • extensions: option<list<(string, string)>>
  • -

    record message

    -

    A message with a binary payload and additional information

    -
    Record Fields
    -
      -
    • -

      topic: channel

      -

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

    • -
    • -

      content-type: option<string>

      -

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

    • -
    • -

      reply-to: option<string>

      -

      An optional topic for use in request/response scenarios. Senders and consumers of -messages must not assume that this field is set and should handle it in their code -accordingly. -

    • -
    • -

      data: list<u8>

      -

      An opaque blob of data -

    • -
    • -

      metadata: option<list<(string, string)>>

      -

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

    • -
    -
    +

    resource message

    +

    A message with a binary payload and additional information

    Functions

    [static]client.connect: func

    Params
    @@ -91,6 +63,100 @@ message +

    [constructor]message: func

    +
    Params
    +
      +
    • topic: channel
    • +
    • data: list<u8>
    • +
    • content-type: option<string>
    • +
    • metadata: option<list<(string, string)>>
    • +
    +
    Return values
    + +

    [method]message.topic: func

    +

    The topic/subject/channel this message was received or should be sent 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
    +
      +
    • option<string>
    • +
    +

    [method]message.data: func

    +

    An opaque blob of data

    +
    Params
    + +
    Return values
    +
      +
    • list<u8>
    • +
    +

    [method]message.metadata: func

    +

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

    +
    Params
    + +
    Return values
    +
      +
    • option<list<(string, string)>>
    • +
    +

    [method]message.complete: func

    +

    Completes/acks the message

    +

    A message can exist under several statuses: +(1) available: the message is ready to be read, +(2) acquired: the message has been sent to a consumer (but still exists in the queue), +(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, +(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    +
      +
    • deleted,
    • +
    • sent to a dead-letter queue, or
    • +
    • kept in the queue for further processing.
    • +
    +
    Params
    + +
    Return values
    + +

    [method]message.abandon: func

    +

    Abandon/nacks the message

    +

    A message can exist under several statuses: +(1) available: the message is ready to be read, +(2) acquired: the message has been sent to a consumer (but still exists in the queue), +(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, +(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    +
      +
    • deleted,
    • +
    • sent to a dead-letter queue, or
    • +
    • kept in the queue for further processing.
    • +
    +
    Params
    + +
    Return values
    +

    Import interface wasi:messaging/producer@0.2.0-draft

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


    @@ -116,28 +182,18 @@ override the channel/topic in the message.

    Return values

    Import interface wasi:messaging/consumer@0.2.0-draft

    -

    The consumer interface allows a guest to dynamically update its subscriptions and configuration -as well as functionality for completing (acking) or abandoning (nacking) messages.

    +

    The consumer interface allows a guest to dynamically update its subscriptions and configuration


    Types

    -

    type client

    -

    client

    -

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

    -#### `type channel` -[`channel`](#channel) -

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

    type error

    +

    error

    #### `type guest-configuration` [`guest-configuration`](#guest_configuration) @@ -165,31 +221,3 @@ enforce it via the credentials used to connect to the service.

    -

    complete-message: func

    -

    A message can exist under several statuses: -(1) available: the message is ready to be read, -(2) acquired: the message has been sent to a consumer (but still exists in the queue), -(3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, -(4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:

    -
      -
    • deleted,
    • -
    • sent to a dead-letter queue, or
    • -
    • kept in the queue for further processing.
    • -
    -
    Params
    - -
    Return values
    - -

    abandon-message: func

    -
    Params
    - -
    Return values
    - diff --git a/messaging.md b/messaging.md index 9765a8e..2702f43 100644 --- a/messaging.md +++ b/messaging.md @@ -56,36 +56,8 @@ Kafka/EventHubs, QoS etc.).

  • channels: list<channel>
  • extensions: option<list<(string, string)>>
  • -

    record message

    -

    A message with a binary payload and additional information

    -
    Record Fields
    -
      -
    • -

      topic: channel

      -

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

    • -
    • -

      content-type: option<string>

      -

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

    • -
    • -

      reply-to: option<string>

      -

      An optional topic for use in request/response scenarios. Senders and consumers of -messages must not assume that this field is set and should handle it in their code -accordingly. -

    • -
    • -

      data: list<u8>

      -

      An opaque blob of data -

    • -
    • -

      metadata: option<list<(string, string)>>

      -

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

    • -
    -
    +

    resource message

    +

    A message with a binary payload and additional information

    Functions

    [static]client.connect: func

    Params
    @@ -96,6 +68,100 @@ message +

    [constructor]message: func

    +
    Params
    +
      +
    • topic: channel
    • +
    • data: list<u8>
    • +
    • content-type: option<string>
    • +
    • metadata: option<list<(string, string)>>
    • +
    +
    Return values
    + +

    [method]message.topic: func

    +

    The topic/subject/channel this message was received or should be sent 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
    +
      +
    • option<string>
    • +
    +

    [method]message.data: func

    +

    An opaque blob of data

    +
    Params
    + +
    Return values
    +
      +
    • list<u8>
    • +
    +

    [method]message.metadata: func

    +

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

    +
    Params
    + +
    Return values
    +
      +
    • option<list<(string, string)>>
    • +
    +

    [method]message.complete: func

    +

    Completes/acks the message

    +

    A message can exist under several statuses: +(1) available: the message is ready to be read, +(2) acquired: the message has been sent to a consumer (but still exists in the queue), +(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, +(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    +
      +
    • deleted,
    • +
    • sent to a dead-letter queue, or
    • +
    • kept in the queue for further processing.
    • +
    +
    Params
    + +
    Return values
    + +

    [method]message.abandon: func

    +

    Abandon/nacks the message

    +

    A message can exist under several statuses: +(1) available: the message is ready to be read, +(2) acquired: the message has been sent to a consumer (but still exists in the queue), +(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, +(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    +
      +
    • deleted,
    • +
    • sent to a dead-letter queue, or
    • +
    • kept in the queue for further processing.
    • +
    +
    Params
    + +
    Return values
    +

    Import interface wasi:messaging/producer@0.2.0-draft

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


    @@ -121,28 +187,18 @@ override the channel/topic in the message.

    Return values

    Import interface wasi:messaging/consumer@0.2.0-draft

    -

    The consumer interface allows a guest to dynamically update its subscriptions and configuration -as well as functionality for completing (acking) or abandoning (nacking) messages.

    +

    The consumer interface allows a guest to dynamically update its subscriptions and configuration


    Types

    -

    type client

    -

    client

    -

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

    -#### `type channel` -[`channel`](#channel) -

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

    type error

    +

    error

    #### `type guest-configuration` [`guest-configuration`](#guest_configuration) @@ -170,34 +226,6 @@ enforce it via the credentials used to connect to the service.

    -

    complete-message: func

    -

    A message can exist under several statuses: -(1) available: the message is ready to be read, -(2) acquired: the message has been sent to a consumer (but still exists in the queue), -(3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, -(4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:

    -
      -
    • deleted,
    • -
    • sent to a dead-letter queue, or
    • -
    • kept in the queue for further processing.
    • -
    -
    Params
    - -
    Return values
    - -

    abandon-message: func

    -
    Params
    - -
    Return values
    -

    Export interface wasi:messaging/guest@0.2.0-draft


    Types

    @@ -214,10 +242,11 @@ enforce it via the credentials used to connect to the service.

    Functions

    handler: func

    Whenever this guest receives a message in one of the subscribed channels, the message is -sent to this handler

    +sent to this handler. The guest is responsible for matching on the channel and handling the +message accordingly.

    Params
    Return values
      diff --git a/wit/consumer.wit b/wit/consumer.wit index 76c105a..a229114 100644 --- a/wit/consumer.wit +++ b/wit/consumer.wit @@ -1,7 +1,6 @@ /// The consumer interface allows a guest to dynamically update its subscriptions and configuration -/// as well as functionality for completing (acking) or abandoning (nacking) messages. interface consumer { - use types.{client, message, channel, error, guest-configuration}; + use types.{error, guest-configuration}; /// 'Fit-all' type function for updating a guest's configuration – this could be useful for: /// - unsubscribing from a channel, @@ -15,15 +14,4 @@ interface consumer { /// should validate that the configured topics are valid topics the guest should have access to or /// enforce it via the credentials used to connect to the service. update-guest-configuration: func(gc: guest-configuration) -> result<_, error>; - - /// A message can exist under several statuses: - /// (1) available: the message is ready to be read, - /// (2) acquired: the message has been sent to a consumer (but still exists in the queue), - /// (3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, - /// (4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be: - /// - deleted, - /// - sent to a dead-letter queue, or - /// - kept in the queue for further processing. - complete-message: func(m: message) -> result<_, error>; - abandon-message: func(m: message) -> result<_, error>; } diff --git a/wit/guest.wit b/wit/guest.wit index 9138149..b4c8eea 100644 --- a/wit/guest.wit +++ b/wit/guest.wit @@ -2,6 +2,7 @@ interface guest { use types.{message, guest-configuration, error}; /// Whenever this guest receives a message in one of the subscribed channels, the message is - /// sent to this handler - handler: func(ms: list) -> result<_, error>; + /// sent to this handler. The guest is responsible for matching on the channel and handling the + /// message accordingly. + handler: func(ms: message) -> result<_, error>; } diff --git a/wit/producer.wit b/wit/producer.wit index 99931d3..0bf388b 100644 --- a/wit/producer.wit +++ b/wit/producer.wit @@ -4,5 +4,5 @@ interface producer { /// Sends a message to the given channel/topic. If the channel/topic is not empty, it will /// override the channel/topic in the message. - send: func(c: client, ch: channel, m: list) -> result<_, error>; + send: func(c: client, ch: channel, m: message) -> result<_, error>; } diff --git a/wit/request-reply.wit b/wit/request-reply.wit index bf08022..513ea8f 100644 --- a/wit/request-reply.wit +++ b/wit/request-reply.wit @@ -7,12 +7,10 @@ interface request-reply { /// Performs a blocking request/reply operation with an optional timeout. If the timeout value /// is not set, then the request/reply operation will block indefinitely. - /// - /// Please note that implementations that provide `wasi:messaging` are responsible for ensuring - /// that guests are not allowed to subscribe to channels that they are not configured to - /// subscribe to (or have access to). Failure to do so can result in possible breakout or access - /// to resources that are not intended to be accessible to the guest. This means implementations - /// should validate that the reply-to field is a valid topic the guest should have access to or - /// enforce it via the credentials used to connect to the service. request: func(c: client, msg: message, timeout-ms: option) -> result>, error>; + + /// Replies to the given message with the given response message. The details of which channel + /// 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. + reply: func(reply-to: borrow, reply: message) -> result<_, error>; } \ No newline at end of file diff --git a/wit/types.wit b/wit/types.wit index 0af0012..c408f53 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -34,20 +34,41 @@ interface types { } /// A message with a binary payload and additional information - record message { + resource message { + constructor(topic: channel, data: list, content-type: option, metadata: option>>); /// The topic/subject/channel this message was received or should be sent on - topic: channel, + topic: func() -> channel; /// An optional content-type describing the format of the data in the message. This is /// sometimes described as the "format" type - content-type: option, - /// An optional topic for use in request/response scenarios. Senders and consumers of - /// messages must not assume that this field is set and should handle it in their code - /// accordingly. - reply-to: option, + content-type: func() -> option; /// An opaque blob of data - data: list, + data: func() -> list; /// Optional metadata (also called headers or attributes in some systems) attached to the /// message - metadata: option>> + metadata: func() -> option>>; + + /// Completes/acks the message + /// + /// A message can exist under several statuses: + /// (1) available: the message is ready to be read, + /// (2) acquired: the message has been sent to a consumer (but still exists in the queue), + /// (3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, + /// (4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be: + /// - deleted, + /// - sent to a dead-letter queue, or + /// - kept in the queue for further processing. + complete: func() -> result<_, error>; + + /// Abandon/nacks the message + /// + /// A message can exist under several statuses: + /// (1) available: the message is ready to be read, + /// (2) acquired: the message has been sent to a consumer (but still exists in the queue), + /// (3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, + /// (4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be: + /// - deleted, + /// - sent to a dead-letter queue, or + /// - kept in the queue for further processing. + abandon: func() -> result<_, error>; } } \ No newline at end of file From d491285650d5389c3f6b0d07c2ec8197de2cd50e Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Wed, 12 Jun 2024 10:29:00 -0600 Subject: [PATCH 4/7] feat(types): Updates the message type to have configurable fields Also removes extensions as a guest configuration option (for now) Signed-off-by: Taylor Thomas --- imports-request-reply.md | 34 +++++++++++++++++++++++++++++++--- imports.md | 34 +++++++++++++++++++++++++++++++--- messaging.md | 34 +++++++++++++++++++++++++++++++--- wit/types.wit | 13 ++++++++++--- 4 files changed, 103 insertions(+), 12 deletions(-) diff --git a/imports-request-reply.md b/imports-request-reply.md index d183745..17b01f3 100644 --- a/imports-request-reply.md +++ b/imports-request-reply.md @@ -45,12 +45,10 @@ action. This error is mainly used when calling `update-guest-configuration`.

      The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

      record guest-configuration

      Configuration includes a required list of channels the guest is subscribing to, and an -optional list of extensions key-value pairs (e.g., partitions/offsets to read from in -Kafka/EventHubs, QoS etc.).

      +optional list of extensions key-value pairs

      Record Fields
      • channels: list<channel>
      • -
      • extensions: option<list<(string, string)>>

      resource message

      A message with a binary payload and additional information

      @@ -86,6 +84,13 @@ Kafka/EventHubs, QoS etc.).

      +

      [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

      @@ -97,6 +102,14 @@ sometimes described as the "format" type

      • option<string>
      +

      [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
      +
        +
      • self: borrow<message>
      • +
      • content-type: string
      • +

      [method]message.data: func

      An opaque blob of data

      Params
      @@ -107,6 +120,13 @@ sometimes described as the "format" type

      • list<u8>
      +

      [method]message.set-data: func

      +

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

      +
      Params
      +
        +
      • self: borrow<message>
      • +
      • data: list<u8>
      • +

      [method]message.metadata: func

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

      @@ -118,6 +138,14 @@ message

      • option<list<(string, string)>>
      +

      [method]message.add-metadata: func

      +

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

      +
      Params
      +
        +
      • self: borrow<message>
      • +
      • key: string
      • +
      • value: string
      • +

      [method]message.complete: func

      Completes/acks the message

      A message can exist under several statuses: diff --git a/imports.md b/imports.md index 384da07..40667d7 100644 --- a/imports.md +++ b/imports.md @@ -44,12 +44,10 @@ action. This error is mainly used when calling `update-guest-configuration`.

      The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

      record guest-configuration

      Configuration includes a required list of channels the guest is subscribing to, and an -optional list of extensions key-value pairs (e.g., partitions/offsets to read from in -Kafka/EventHubs, QoS etc.).

      +optional list of extensions key-value pairs

      Record Fields
      • channels: list<channel>
      • -
      • extensions: option<list<(string, string)>>

      resource message

      A message with a binary payload and additional information

      @@ -85,6 +83,13 @@ Kafka/EventHubs, QoS etc.).

      +

      [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

      @@ -96,6 +101,14 @@ sometimes described as the "format" type

      • option<string>
      +

      [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
      +
        +
      • self: borrow<message>
      • +
      • content-type: string
      • +

      [method]message.data: func

      An opaque blob of data

      Params
      @@ -106,6 +119,13 @@ sometimes described as the "format" type

      • list<u8>
      +

      [method]message.set-data: func

      +

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

      +
      Params
      +
        +
      • self: borrow<message>
      • +
      • data: list<u8>
      • +

      [method]message.metadata: func

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

      @@ -117,6 +137,14 @@ message

      • option<list<(string, string)>>
      +

      [method]message.add-metadata: func

      +

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

      +
      Params
      +
        +
      • self: borrow<message>
      • +
      • key: string
      • +
      • value: string
      • +

      [method]message.complete: func

      Completes/acks the message

      A message can exist under several statuses: diff --git a/messaging.md b/messaging.md index 2702f43..031f682 100644 --- a/messaging.md +++ b/messaging.md @@ -49,12 +49,10 @@ action. This error is mainly used when calling `update-guest-configuration`.

      The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

      record guest-configuration

      Configuration includes a required list of channels the guest is subscribing to, and an -optional list of extensions key-value pairs (e.g., partitions/offsets to read from in -Kafka/EventHubs, QoS etc.).

      +optional list of extensions key-value pairs

      Record Fields
      • channels: list<channel>
      • -
      • extensions: option<list<(string, string)>>

      resource message

      A message with a binary payload and additional information

      @@ -90,6 +88,13 @@ Kafka/EventHubs, QoS etc.).

      +

      [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

      @@ -101,6 +106,14 @@ sometimes described as the "format" type

      • option<string>
      +

      [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
      +
        +
      • self: borrow<message>
      • +
      • content-type: string
      • +

      [method]message.data: func

      An opaque blob of data

      Params
      @@ -111,6 +124,13 @@ sometimes described as the "format" type

      • list<u8>
      +

      [method]message.set-data: func

      +

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

      +
      Params
      +
        +
      • self: borrow<message>
      • +
      • data: list<u8>
      • +

      [method]message.metadata: func

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

      @@ -122,6 +142,14 @@ message

      • option<list<(string, string)>>
      +

      [method]message.add-metadata: func

      +

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

      +
      Params
      +
        +
      • self: borrow<message>
      • +
      • key: string
      • +
      • value: string
      • +

      [method]message.complete: func

      Completes/acks the message

      A message can exist under several statuses: diff --git a/wit/types.wit b/wit/types.wit index c408f53..fe64e47 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -26,11 +26,9 @@ interface types { type channel = string; /// Configuration includes a required list of channels the guest is subscribing to, and an - /// optional list of extensions key-value pairs (e.g., partitions/offsets to read from in - /// Kafka/EventHubs, QoS etc.). + /// optional list of extensions key-value pairs record guest-configuration { channels: list, - extensions: option>> } /// A message with a binary payload and additional information @@ -38,14 +36,23 @@ interface types { constructor(topic: channel, data: list, content-type: option, metadata: option>>); /// The topic/subject/channel this message was received or should be sent on topic: func() -> channel; + /// Set the topic/subject/channel this message should be sent on + set-topic: func(topic: channel); /// 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 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); /// Completes/acks the message /// From 536043da31147c77702ac4675e85464ae685be9c Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Wed, 12 Jun 2024 11:18:41 -0600 Subject: [PATCH 5/7] feat(types): Renames guest config to just plain simple `config` In many of the interfaces out there right now, we've moved more towards just calling these things config Signed-off-by: Taylor Thomas --- imports-request-reply.md | 19 +++++++++---------- imports.md | 19 +++++++++---------- messaging.md | 23 +++++++++++------------ wit/consumer.wit | 4 ++-- wit/guest.wit | 2 +- wit/types.wit | 7 +++---- 6 files changed, 35 insertions(+), 39 deletions(-) diff --git a/imports-request-reply.md b/imports-request-reply.md index 17b01f3..dcde926 100644 --- a/imports-request-reply.md +++ b/imports-request-reply.md @@ -22,7 +22,7 @@

      unauthorized

      The requested option is not authorized. This could be a topic it doesn't have permission to subscribe to, or a permission it doesn't have to perform a specific -action. This error is mainly used when calling `update-guest-configuration`. +action. This error is mainly used when calling `update-config`.

    • timeout

      @@ -43,12 +43,11 @@ action. This error is mainly used when calling `update-guest-configuration`. - publish-subscribe channel, which is a broadcast channel, and - point-to-point channel, which is a unicast channel.

      The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

      -

      record guest-configuration

      -

      Configuration includes a required list of channels the guest is subscribing to, and an -optional list of extensions key-value pairs

      +

      record config

      +

      Configuration includes a required list of channels the guest is subscribing to

      Record Fields

      resource message

      A message with a binary payload and additional information

      @@ -268,12 +267,12 @@ override the channel/topic in the message.

      type error

      error

      -#### `type guest-configuration` -[`guest-configuration`](#guest_configuration) +#### `type config` +[`config`](#config)

      ----

      Functions

      -

      update-guest-configuration: func

      +

      update-config: func

      'Fit-all' type function for updating a guest's configuration – this could be useful for:

      • unsubscribing from a channel,
      • @@ -288,9 +287,9 @@ should validate that the configured topics are valid topics the guest should hav enforce it via the credentials used to connect to the service.

        Params
        Return values
        diff --git a/imports.md b/imports.md index 40667d7..b87ab8e 100644 --- a/imports.md +++ b/imports.md @@ -21,7 +21,7 @@

        unauthorized

        The requested option is not authorized. This could be a topic it doesn't have permission to subscribe to, or a permission it doesn't have to perform a specific -action. This error is mainly used when calling `update-guest-configuration`. +action. This error is mainly used when calling `update-config`.

      • timeout

        @@ -42,12 +42,11 @@ action. This error is mainly used when calling `update-guest-configuration`. - publish-subscribe channel, which is a broadcast channel, and - point-to-point channel, which is a unicast channel.

        The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

        -

        record guest-configuration

        -

        Configuration includes a required list of channels the guest is subscribing to, and an -optional list of extensions key-value pairs

        +

        record config

        +

        Configuration includes a required list of channels the guest is subscribing to

        Record Fields

        resource message

        A message with a binary payload and additional information

        @@ -223,12 +222,12 @@ override the channel/topic in the message.

        type error

        error

        -#### `type guest-configuration` -[`guest-configuration`](#guest_configuration) +#### `type config` +[`config`](#config)

        ----

        Functions

        -

        update-guest-configuration: func

        +

        update-config: func

        'Fit-all' type function for updating a guest's configuration – this could be useful for:

        • unsubscribing from a channel,
        • @@ -243,9 +242,9 @@ should validate that the configured topics are valid topics the guest should hav enforce it via the credentials used to connect to the service.

          Params
          Return values
          diff --git a/messaging.md b/messaging.md index 031f682..31cce91 100644 --- a/messaging.md +++ b/messaging.md @@ -26,7 +26,7 @@

          unauthorized

          The requested option is not authorized. This could be a topic it doesn't have permission to subscribe to, or a permission it doesn't have to perform a specific -action. This error is mainly used when calling `update-guest-configuration`. +action. This error is mainly used when calling `update-config`.

        • timeout

          @@ -47,12 +47,11 @@ action. This error is mainly used when calling `update-guest-configuration`. - publish-subscribe channel, which is a broadcast channel, and - point-to-point channel, which is a unicast channel.

          The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

          -

          record guest-configuration

          -

          Configuration includes a required list of channels the guest is subscribing to, and an -optional list of extensions key-value pairs

          +

          record config

          +

          Configuration includes a required list of channels the guest is subscribing to

          Record Fields

          resource message

          A message with a binary payload and additional information

          @@ -228,12 +227,12 @@ override the channel/topic in the message.

          type error

          error

          -#### `type guest-configuration` -[`guest-configuration`](#guest_configuration) +#### `type config` +[`config`](#config)

          ----

          Functions

          -

          update-guest-configuration: func

          +

          update-config: func

          'Fit-all' type function for updating a guest's configuration – this could be useful for:

          • unsubscribing from a channel,
          • @@ -248,11 +247,11 @@ should validate that the configured topics are valid topics the guest should hav enforce it via the credentials used to connect to the service.

            Params
            Return values

            Export interface wasi:messaging/guest@0.2.0-draft


            @@ -260,8 +259,8 @@ enforce it via the credentials used to connect to the service.

            type message

            message

            -#### `type guest-configuration` -[`guest-configuration`](#guest_configuration) +#### `type config` +[`config`](#config)

            #### `type error` [`error`](#error) diff --git a/wit/consumer.wit b/wit/consumer.wit index a229114..48115cc 100644 --- a/wit/consumer.wit +++ b/wit/consumer.wit @@ -1,6 +1,6 @@ /// The consumer interface allows a guest to dynamically update its subscriptions and configuration interface consumer { - use types.{error, guest-configuration}; + use types.{error, config}; /// 'Fit-all' type function for updating a guest's configuration – this could be useful for: /// - unsubscribing from a channel, @@ -13,5 +13,5 @@ interface consumer { /// to resources that are not intended to be accessible to the guest. This means implementations /// should validate that the configured topics are valid topics the guest should have access to or /// enforce it via the credentials used to connect to the service. - update-guest-configuration: func(gc: guest-configuration) -> result<_, error>; + update-config: func(gc: config) -> result<_, error>; } diff --git a/wit/guest.wit b/wit/guest.wit index b4c8eea..004477d 100644 --- a/wit/guest.wit +++ b/wit/guest.wit @@ -1,5 +1,5 @@ interface guest { - use types.{message, guest-configuration, error}; + use types.{message, config, error}; /// 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 diff --git a/wit/types.wit b/wit/types.wit index fe64e47..4199913 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -8,7 +8,7 @@ interface types { variant error { /// The requested option is not authorized. This could be a topic it doesn't have /// permission to subscribe to, or a permission it doesn't have to perform a specific - /// action. This error is mainly used when calling `update-guest-configuration`. + /// action. This error is mainly used when calling `update-config`. unauthorized, /// The request or operation timed out. timeout, @@ -25,9 +25,8 @@ interface types { /// The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue. type channel = string; - /// Configuration includes a required list of channels the guest is subscribing to, and an - /// optional list of extensions key-value pairs - record guest-configuration { + /// Configuration includes a required list of channels the guest is subscribing to + record config { channels: list, } From 20ddd688d0b11ba20f3296c64b1085588767f05d Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Thu, 13 Jun 2024 16:51:16 -0600 Subject: [PATCH 6/7] feat(*): Additional changes to request/reply for streamlining Also removes the channel parameter I forgot to remove in a previous commit Signed-off-by: Taylor Thomas --- imports-request-reply.md | 134 +++++++++++++++------------------------ imports.md | 89 ++++---------------------- messaging.md | 106 ++++++------------------------- wit/consumer.wit | 9 +-- wit/guest.wit | 10 +-- wit/messaging.wit | 2 +- wit/producer.wit | 7 +- wit/request-reply.wit | 24 ++++++- wit/types.wit | 46 ++------------ 9 files changed, 126 insertions(+), 301 deletions(-) diff --git a/imports-request-reply.md b/imports-request-reply.md index dcde926..b7e743c 100644 --- a/imports-request-reply.md +++ b/imports-request-reply.md @@ -22,7 +22,7 @@

            unauthorized

            The requested option is not authorized. This could be a topic it doesn't have permission to subscribe to, or a permission it doesn't have to perform a specific -action. This error is mainly used when calling `update-config`. +action. This error is mainly used when calling `set-subscriptions` on a guest.

          • timeout

            @@ -33,22 +33,14 @@ action. This error is mainly used when calling `update-config`.

            An error occurred with the connection. Includes a message for additional context

          • +

            abandoned: string

            +

            Work on the message was abandoned for the given reason +

          • +
          • other: string

            A catch all for other types of errors

          -

          type channel

          -

          string

          -

          There are two types of channels: -- publish-subscribe channel, which is a broadcast channel, and -- point-to-point channel, which is a unicast channel. -

          The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

          -

          record config

          -

          Configuration includes a required list of channels the guest is subscribing to

          -
          Record Fields
          -

          resource message

          A message with a binary payload and additional information

          Functions

          @@ -64,10 +56,8 @@ action. This error is mainly used when calling `update-config`.

          [constructor]message: func

          Params
            -
          • topic: channel
          • +
          • topic: string
          • data: list<u8>
          • -
          • content-type: option<string>
          • -
          • metadata: option<list<(string, string)>>
          Return values
            @@ -81,14 +71,14 @@ action. This error is mainly used when calling `update-config`.
          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 @@ -145,46 +135,6 @@ message

        • key: string
        • value: string
        -

        [method]message.complete: func

        -

        Completes/acks the message

        -

        A message can exist under several statuses: -(1) available: the message is ready to be read, -(2) acquired: the message has been sent to a consumer (but still exists in the queue), -(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, -(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

        -
          -
        • deleted,
        • -
        • sent to a dead-letter queue, or
        • -
        • kept in the queue for further processing.
        • -
        -
        Params
        - -
        Return values
        - -

        [method]message.abandon: func

        -

        Abandon/nacks the message

        -

        A message can exist under several statuses: -(1) available: the message is ready to be read, -(2) acquired: the message has been sent to a consumer (but still exists in the queue), -(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, -(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

        -
          -
        • deleted,
        • -
        • sent to a dead-letter queue, or
        • -
        • kept in the queue for further processing.
        • -
        -
        Params
        - -
        Return values
        -

        Import interface wasi:messaging/request-reply@0.2.0-draft

        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 @@ -201,20 +151,53 @@ included it as a core interface.

        #### `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
        +

        request: func

        -

        Performs a blocking request/reply operation with an optional timeout. If the timeout value -is not set, then the request/reply operation will block indefinitely.

        +

        Performs a blocking request/reply operation with an optional set of request options. This +returns only the first reply received or a timeout . If more than one reply is expected, then the +request-multi function should be used instead.

        Params
        +
        Return values
        + +

        request-multi: func

        +

        Performs a blocking request/reply operation with an optional set of request options. This +returns all replies received up to the number of expected replies. It is recommended to use +a request-options with the timeout set to ensure that the operation does not block +indefinitely.

        +
        Params
        +
        Return values

        reply: func

        Replies to the given message with the given response message. The details of which channel @@ -236,9 +219,6 @@ handled in the best way possible for the underlying messaging system.

        type client

        client

        -#### `type channel` -[`channel`](#channel) -

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

        @@ -248,12 +228,10 @@ handled in the best way possible for the underlying messaging system.

        ----

        Functions

        send: func

        -

        Sends a message to the given channel/topic. If the channel/topic is not empty, it will -override the channel/topic in the message.

        +

        Sends the message using the given client.

        Params
        Return values
        @@ -267,18 +245,10 @@ override the channel/topic in the message.

        type error

        error

        -#### `type config` -[`config`](#config) -

        ----

        Functions

        -

        update-config: func

        -

        'Fit-all' type function for updating a guest's configuration – this could be useful for:

        -
          -
        • unsubscribing from a channel,
        • -
        • checkpointing,
        • -
        • etc..
        • -
        +

        set-subscriptions: func

        +

        Set the current subscriptions for this guest.

        Please note that implementations that provide wasi:messaging are responsible for ensuring that guests are not allowed to subscribe to channels that they are not configured to subscribe to (or have access to). Failure to do so can result in possible breakout or access @@ -287,9 +257,9 @@ should validate that the configured topics are valid topics the guest should hav enforce it via the credentials used to connect to the service.

        Params
          -
        • gc: config
        • +
        • topics: list<string>
        Return values
        diff --git a/imports.md b/imports.md index b87ab8e..4e04c71 100644 --- a/imports.md +++ b/imports.md @@ -21,7 +21,7 @@

        unauthorized

        The requested option is not authorized. This could be a topic it doesn't have permission to subscribe to, or a permission it doesn't have to perform a specific -action. This error is mainly used when calling `update-config`. +action. This error is mainly used when calling `set-subscriptions` on a guest.

      • timeout

        @@ -32,22 +32,14 @@ action. This error is mainly used when calling `update-config`.

        An error occurred with the connection. Includes a message for additional context

      • +

        abandoned: string

        +

        Work on the message was abandoned for the given reason +

      • +
      • other: string

        A catch all for other types of errors

      -

      type channel

      -

      string

      -

      There are two types of channels: -- publish-subscribe channel, which is a broadcast channel, and -- point-to-point channel, which is a unicast channel. -

      The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

      -

      record config

      -

      Configuration includes a required list of channels the guest is subscribing to

      -
      Record Fields
      -

      resource message

      A message with a binary payload and additional information

      Functions

      @@ -63,10 +55,8 @@ action. This error is mainly used when calling `update-config`.

      [constructor]message: func

      Params
        -
      • topic: channel
      • +
      • topic: string
      • data: list<u8>
      • -
      • content-type: option<string>
      • -
      • metadata: option<list<(string, string)>>
      Return values
        @@ -80,14 +70,14 @@ action. This error is mainly used when calling `update-config`.
      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 @@ -144,46 +134,6 @@ message

    • key: string
    • value: string
    -

    [method]message.complete: func

    -

    Completes/acks the message

    -

    A message can exist under several statuses: -(1) available: the message is ready to be read, -(2) acquired: the message has been sent to a consumer (but still exists in the queue), -(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, -(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    -
      -
    • deleted,
    • -
    • sent to a dead-letter queue, or
    • -
    • kept in the queue for further processing.
    • -
    -
    Params
    - -
    Return values
    - -

    [method]message.abandon: func

    -

    Abandon/nacks the message

    -

    A message can exist under several statuses: -(1) available: the message is ready to be read, -(2) acquired: the message has been sent to a consumer (but still exists in the queue), -(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, -(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    -
      -
    • deleted,
    • -
    • sent to a dead-letter queue, or
    • -
    • kept in the queue for further processing.
    • -
    -
    Params
    - -
    Return values
    -

    Import interface wasi:messaging/producer@0.2.0-draft

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


    @@ -191,9 +141,6 @@ message

    type client

    client

    -#### `type channel` -[`channel`](#channel) -

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

    @@ -203,12 +150,10 @@ message

    ----

    Functions

    send: func

    -

    Sends a message to the given channel/topic. If the channel/topic is not empty, it will -override the channel/topic in the message.

    +

    Sends the message using the given client.

    Params
    Return values
    @@ -222,18 +167,10 @@ override the channel/topic in the message.

    type error

    error

    -#### `type config` -[`config`](#config) -

    ----

    Functions

    -

    update-config: func

    -

    'Fit-all' type function for updating a guest's configuration – this could be useful for:

    -
      -
    • unsubscribing from a channel,
    • -
    • checkpointing,
    • -
    • etc..
    • -
    +

    set-subscriptions: func

    +

    Set the current subscriptions for this guest.

    Please note that implementations that provide wasi:messaging are responsible for ensuring that guests are not allowed to subscribe to channels that they are not configured to subscribe to (or have access to). Failure to do so can result in possible breakout or access @@ -242,9 +179,9 @@ should validate that the configured topics are valid topics the guest should hav enforce it via the credentials used to connect to the service.

    Params
      -
    • gc: config
    • +
    • topics: list<string>
    Return values
    diff --git a/messaging.md b/messaging.md index 31cce91..bb5091e 100644 --- a/messaging.md +++ b/messaging.md @@ -9,7 +9,7 @@
  • Exports:
  • @@ -26,7 +26,7 @@

    unauthorized

    The requested option is not authorized. This could be a topic it doesn't have permission to subscribe to, or a permission it doesn't have to perform a specific -action. This error is mainly used when calling `update-config`. +action. This error is mainly used when calling `set-subscriptions` on a guest.

  • timeout

    @@ -37,22 +37,14 @@ action. This error is mainly used when calling `update-config`.

    An error occurred with the connection. Includes a message for additional context

  • +

    abandoned: string

    +

    Work on the message was abandoned for the given reason +

  • +
  • other: string

    A catch all for other types of errors

  • -

    type channel

    -

    string

    -

    There are two types of channels: -- publish-subscribe channel, which is a broadcast channel, and -- point-to-point channel, which is a unicast channel. -

    The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.

    -

    record config

    -

    Configuration includes a required list of channels the guest is subscribing to

    -
    Record Fields
    -

    resource message

    A message with a binary payload and additional information

    Functions

    @@ -68,10 +60,8 @@ action. This error is mainly used when calling `update-config`.

    [constructor]message: func

    Params
      -
    • topic: channel
    • +
    • topic: string
    • data: list<u8>
    • -
    • content-type: option<string>
    • -
    • metadata: option<list<(string, string)>>
    Return values
      @@ -85,14 +75,14 @@ action. This error is mainly used when calling `update-config`.
    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 @@ -149,46 +139,6 @@ message

  • key: string
  • value: string
  • -

    [method]message.complete: func

    -

    Completes/acks the message

    -

    A message can exist under several statuses: -(1) available: the message is ready to be read, -(2) acquired: the message has been sent to a consumer (but still exists in the queue), -(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, -(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    -
      -
    • deleted,
    • -
    • sent to a dead-letter queue, or
    • -
    • kept in the queue for further processing.
    • -
    -
    Params
    - -
    Return values
    - -

    [method]message.abandon: func

    -

    Abandon/nacks the message

    -

    A message can exist under several statuses: -(1) available: the message is ready to be read, -(2) acquired: the message has been sent to a consumer (but still exists in the queue), -(3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, -(4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be:

    -
      -
    • deleted,
    • -
    • sent to a dead-letter queue, or
    • -
    • kept in the queue for further processing.
    • -
    -
    Params
    - -
    Return values
    -

    Import interface wasi:messaging/producer@0.2.0-draft

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


    @@ -196,9 +146,6 @@ message

    type client

    client

    -#### `type channel` -[`channel`](#channel) -

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

    @@ -208,12 +155,10 @@ message

    ----

    Functions

    send: func

    -

    Sends a message to the given channel/topic. If the channel/topic is not empty, it will -override the channel/topic in the message.

    +

    Sends the message using the given client.

    Params
    Return values
    @@ -227,18 +172,10 @@ override the channel/topic in the message.

    type error

    error

    -#### `type config` -[`config`](#config) -

    ----

    Functions

    -

    update-config: func

    -

    'Fit-all' type function for updating a guest's configuration – this could be useful for:

    -
      -
    • unsubscribing from a channel,
    • -
    • checkpointing,
    • -
    • etc..
    • -
    +

    set-subscriptions: func

    +

    Set the current subscriptions for this guest.

    Please note that implementations that provide wasi:messaging are responsible for ensuring that guests are not allowed to subscribe to channels that they are not configured to subscribe to (or have access to). Failure to do so can result in possible breakout or access @@ -247,35 +184,34 @@ should validate that the configured topics are valid topics the guest should hav enforce it via the credentials used to connect to the service.

    Params
      -
    • gc: config
    • +
    • topics: list<string>
    Return values
    -

    Export interface wasi:messaging/guest@0.2.0-draft

    +

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


    Types

    type message

    message

    -#### `type config` -[`config`](#config) -

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

    ----

    Functions

    -

    handler: func

    +

    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.

    +message accordingly. An Ok result indicates that the message was handled successfully and +should be considered processed. If explicitly abandoning the message, the guest should return +error::abandoned with a message indicating why.

    Params
    Return values
    diff --git a/wit/consumer.wit b/wit/consumer.wit index 48115cc..e8bede6 100644 --- a/wit/consumer.wit +++ b/wit/consumer.wit @@ -1,11 +1,8 @@ /// The consumer interface allows a guest to dynamically update its subscriptions and configuration interface consumer { - use types.{error, config}; + use types.{error}; - /// 'Fit-all' type function for updating a guest's configuration – this could be useful for: - /// - unsubscribing from a channel, - /// - checkpointing, - /// - etc.. + /// Set the current subscriptions for this guest. /// /// Please note that implementations that provide `wasi:messaging` are responsible for ensuring /// that guests are not allowed to subscribe to channels that they are not configured to @@ -13,5 +10,5 @@ interface consumer { /// to resources that are not intended to be accessible to the guest. This means implementations /// should validate that the configured topics are valid topics the guest should have access to or /// enforce it via the credentials used to connect to the service. - update-config: func(gc: config) -> result<_, error>; + set-subscriptions: func(topics: list) -> result<_, error>; } diff --git a/wit/guest.wit b/wit/guest.wit index 004477d..c2c6dd7 100644 --- a/wit/guest.wit +++ b/wit/guest.wit @@ -1,8 +1,10 @@ -interface guest { - use types.{message, config, error}; +interface incoming-handler { + use types.{message, error}; /// 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. - handler: func(ms: message) -> result<_, error>; + /// message accordingly. An Ok result indicates that the message was handled successfully and + /// should be considered processed. If explicitly abandoning the message, the guest should return + /// error::abandoned with a message indicating why. + handle: func(ms: message) -> result<_, error>; } diff --git a/wit/messaging.wit b/wit/messaging.wit index 2d18024..d5c3ada 100644 --- a/wit/messaging.wit +++ b/wit/messaging.wit @@ -12,5 +12,5 @@ world imports-request-reply { world messaging { include imports; - export guest; + export incoming-handler; } \ No newline at end of file diff --git a/wit/producer.wit b/wit/producer.wit index 0bf388b..948baa9 100644 --- a/wit/producer.wit +++ b/wit/producer.wit @@ -1,8 +1,7 @@ /// The producer interface is used to send messages to a channel/topic. interface producer { - use types.{client, channel, message, error}; + use types.{client, message, error}; - /// Sends a message to the given channel/topic. If the channel/topic is not empty, it will - /// override the channel/topic in the message. - send: func(c: client, ch: channel, m: message) -> result<_, error>; + /// Sends the message using the given client. + send: func(c: client, m: message) -> result<_, error>; } diff --git a/wit/request-reply.wit b/wit/request-reply.wit index 513ea8f..a680107 100644 --- a/wit/request-reply.wit +++ b/wit/request-reply.wit @@ -5,9 +5,27 @@ interface request-reply { use types.{client, message, error}; - /// Performs a blocking request/reply operation with an optional timeout. If the timeout value - /// is not set, then the request/reply operation will block indefinitely. - request: func(c: client, msg: message, timeout-ms: option) -> result>, 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); + } + + /// Performs a blocking request/reply operation with an optional set of request options. This + /// returns only the first reply received or a timeout . If more than one reply is expected, then the + /// `request-multi` function should be used instead. + request: func(c: borrow, msg: message, opts: option) -> result; + + /// Performs a blocking request/reply operation with an optional set of request options. This + /// returns all replies received up to the number of expected replies. It is recommended to use + /// a `request-options` with the timeout set to ensure that the operation does not block + /// indefinitely. + request-multi: func(c: borrow, msg: message, expected-replies: u32, opts: option) -> result, error>; /// Replies to the given message with the given response message. The details of which channel /// the message is sent to is up to the implementation. This allows for reply to details to be diff --git a/wit/types.wit b/wit/types.wit index 4199913..3bb8d3a 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -8,35 +8,25 @@ interface types { variant error { /// The requested option is not authorized. This could be a topic it doesn't have /// permission to subscribe to, or a permission it doesn't have to perform a specific - /// action. This error is mainly used when calling `update-config`. + /// action. This error is mainly used when calling `set-subscriptions` on a guest. unauthorized, /// The request or operation timed out. timeout, /// An error occurred with the connection. Includes a message for additional context connection(string), + /// Work on the message was abandoned for the given reason + abandoned(string), /// A catch all for other types of errors other(string), } - /// There are two types of channels: - /// - publish-subscribe channel, which is a broadcast channel, and - /// - point-to-point channel, which is a unicast channel. - /// - /// The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue. - type channel = string; - - /// Configuration includes a required list of channels the guest is subscribing to - record config { - channels: list, - } - /// A message with a binary payload and additional information resource message { - constructor(topic: channel, data: list, content-type: option, metadata: option>>); + constructor(topic: string, data: list); /// The topic/subject/channel this message was received or should be sent on - topic: func() -> channel; + topic: func() -> string; /// Set the topic/subject/channel this message should be sent on - set-topic: func(topic: channel); + set-topic: func(topic: string); /// 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; @@ -52,29 +42,5 @@ interface types { 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); - - /// Completes/acks the message - /// - /// A message can exist under several statuses: - /// (1) available: the message is ready to be read, - /// (2) acquired: the message has been sent to a consumer (but still exists in the queue), - /// (3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, - /// (4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be: - /// - deleted, - /// - sent to a dead-letter queue, or - /// - kept in the queue for further processing. - complete: func() -> result<_, error>; - - /// Abandon/nacks the message - /// - /// A message can exist under several statuses: - /// (1) available: the message is ready to be read, - /// (2) acquired: the message has been sent to a consumer (but still exists in the queue), - /// (3) accepted (result of complete): the message has been received and ACK-ed by a consumer and can be safely removed from the queue, - /// (4) rejected (result of abandon): the message has been received and NACK-ed by a consumer, at which point it can be: - /// - deleted, - /// - sent to a dead-letter queue, or - /// - kept in the queue for further processing. - abandon: func() -> result<_, error>; } } \ No newline at end of file From 35582eddc0dcb0ef9f114181ac5b7ad606744e33 Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Wed, 17 Jul 2024 16:27:24 -0600 Subject: [PATCH 7/7] feat(request): Updates request-multi to support scatter/gather operations One of the uses of request-multi is to support a scatter/gather operation. In these cases, you might not know how many requests you are going to receive, so you can't set expected replies. Generally these wait until timeout and then return the results. This commit adds the ability to support all the different use cases for request-multi Signed-off-by: Taylor Thomas --- imports-request-reply.md | 20 ++++++++++++++++---- wit/request-reply.wit | 17 +++++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/imports-request-reply.md b/imports-request-reply.md index b7e743c..659bd3f 100644 --- a/imports-request-reply.md +++ b/imports-request-reply.md @@ -169,6 +169,16 @@ the request/reply operation will block until a message is received in response.<
  • self: borrow<request-options>
  • timeout-ms: u32
  • +

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

    +

    The maximum number of replies to expect before returning. This only applies to +request-multi and is ignored otherwise. If the number of replies is not set and +timeout isn't set, then the operation will block until a message is received in +response (essentially the same behavior as request).

    +
    Params
    +

    request: func

    Performs a blocking request/reply operation with an optional set of request options. This returns only the first reply received or a timeout . If more than one reply is expected, then the @@ -185,14 +195,16 @@ returns only the first reply received or a timeout . If more than one reply is e

    request-multi: func

    Performs a blocking request/reply operation with an optional set of request options. This -returns all replies received up to the number of expected replies. It is recommended to use -a request-options with the timeout set to ensure that the operation does not block -indefinitely.

    +returns all replies received up until timeout or the configured set of expected replies. It +is recommended to use a request-options with the timeout set to ensure that the operation +does not block indefinitely. Unlike request, this function should not return an error on +timeout and should instead return all of the replies received up to that point. This is to +faciliate use in scatter/gather operations where the number of expected replies is not +known.

    Params
    Return values
    diff --git a/wit/request-reply.wit b/wit/request-reply.wit index a680107..7103535 100644 --- a/wit/request-reply.wit +++ b/wit/request-reply.wit @@ -14,6 +14,12 @@ interface request-reply { /// 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. This only applies to + /// `request-multi` and is ignored otherwise. If the number of replies is not set and + /// timeout isn't set, then the operation will block until a message is received in + /// response (essentially the same behavior as `request`). + set-expected-replies: func(expected-replies: u32); } /// Performs a blocking request/reply operation with an optional set of request options. This @@ -22,10 +28,13 @@ interface request-reply { request: func(c: borrow, msg: message, opts: option) -> result; /// Performs a blocking request/reply operation with an optional set of request options. This - /// returns all replies received up to the number of expected replies. It is recommended to use - /// a `request-options` with the timeout set to ensure that the operation does not block - /// indefinitely. - request-multi: func(c: borrow, msg: message, expected-replies: u32, opts: option) -> result, error>; + /// returns all replies received up until timeout or the configured set of expected replies. It + /// is recommended to use a `request-options` with the timeout set to ensure that the operation + /// does not block indefinitely. Unlike request, this function should not return an error on + /// timeout and should instead return all of the replies received up to that point. This is to + /// faciliate use in scatter/gather operations where the number of expected replies is not + /// known. + request-multi: func(c: borrow, msg: message, opts: option) -> result, error>; /// Replies to the given message with the given response message. The details of which channel /// the message is sent to is up to the implementation. This allows for reply to details to be