Skip to content

Commit

Permalink
Initial commit for Request-Reply WebSocket tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
VaishnaviNandakumar committed Nov 24, 2023
1 parent 5439191 commit 79d8051
Showing 1 changed file with 264 additions and 0 deletions.
264 changes: 264 additions & 0 deletions pages/docs/tutorials/websocket/websocket-request-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
---
title: Implement Request Reply in an AsyncAPI Document for a Slack App
description: In this tutorial, you'll learn to write an AsyncAPI document that implements the request reply feature for a Slack application using the WebSocket Protocol.
weight: 220
---

## Introduction
In the [previous]() tutorial, you learnt how to write an AsyncAPI document for a Slackbot `Heart-Counter` that actively monitored reactions associated with a message. In this lesson, let's go a step further. After receiving a reaction, Heart-Counter responds by sending a message back to the Slack server through WebSocket to confirm the event reception.

The interaction where the Slackbot acknowledges the event and replies with a specific response sets the stage for the Request-Reply messaging pattern discussed in this context. The aim of this tutorial is to provide a clear understanding of the Request-Reply Messaging pattern and showcase how AsyncAPI v3 effectively describes it.


```mermaid
sequenceDiagram
participant Heart-Counter
participant Slack Server (Socket Mode)
participant User
User-->>Slack Server (Socket Mode): Adds emoji to a message.
Slack Server (Socket Mode)->>Heart-Counter: Sending "reaction_added" payload
Note left of Heart-Counter: Event received
Heart-Counter->>Slack Server (Socket Mode): Sending acknowledgement
```


## Background context

The inclusion of Request-Reply Messaging Pattern in AsyncAPI v3 is an exciting and a highly anticipated feature. This messaging pattern mirrors a traditional conversation, where one "requester" entity initiates a query or request, and the "responder" entity provides a specific and expected response.
This messaging pattern can work in both synchronous and asynchronous environments and is very beneficial to decouple components by allowing them to operate independently in a distributed system.

```mermaid
sequenceDiagram
participant Client
participant Server
Client->>Server: Request
note right of Client: Continue with other tasks
Server-->>Client: Acknowledgment
Server->>Server: Process Request
Server-->>Client: Reply
```

## Define AsyncAPI version, API information, and server

Begin your AsyncAPI document by detailing the `title`, `version`, and `description` for your Slack app's API. In the `servers` section, you can outline the protocol and provide details about the URLs your application will utilize. As you are building upon the existing `Heart-Counter` document from the [previous]() tutorial, there are no new changed needed in this section.

```
asyncapi: '3.0.0'
info:
title: Implement Request Reply in an AsyncAPI Document for a Slack App
version: '1.0.0'
description: |
The Heart-Counter manages popular messages in a Slack workspace by monitoring message reaction data. It also sends an acknowledgment message back to the Slack Server to indicate it has received the message.
servers:
production:
host: wss-primary.slack.com
pathname: /link
protocol: wss
description: Slack's server in Socket Mode for real-time communication
```

## Define messages

The `messages` component contains all the event types your application will interact with. Each message references the `payload` attribute that represent the structure of the event it is supposed to receive. In the `Heart-Counter` tutorial, you dealt with the `hello` event that was triggered when the WebSocket connection was established and the `reaction` event that was expected when a new reaction-added event was triggered.

In this tutorial, you'll be adding the `acknowledge` message to your AsyncAPI document to represent the acknowledgement sent by the Slack application back to the server to indicate that it has successfully received the message.

```
components:
messages:
reaction:
summary: Action triggered when the channel receives a new reaction-added event
payload:
$ref: '#/components/schemas/reaction'
hello:
summary: Action triggered when a successful WebSocket connection is established
payload:
$ref: '#/components/schemas/hello'
acknowledge:
summary: Acknowledgement response sent to Server
payload:
$ref: '#/components/schemas/acknowledge'
```

## Define schemas

In an AsyncAPI document, `schemas` are used to represent the format and structure of the defined events. In the last tutorial, the `reaction` schema was simplified to just include the `event` payload. However, in this instance, you will be elaborating on the schema for the complete request it is anticipated to receive.

<Remember>
The sample request and response payloads are extracted from Slack's official documentation available [here](https://example.com).
</Remember>

The `acknowledge` schema makes use of the `envelope_id` field in the object you received from the WebSocket represented by the `reaction` schema to send a response back to Slack acknowledging that event has been received.

```
schemas:
hello:
type: object
properties:
type:
type: string
description: A hello string confirming WebSocket connection
const: "hello"
connection_info:
type: object
properties:
app_id:
type: string
num_connections:
type: integer
debug_info:
type: object
properties:
host:
type: string
started:
type: string
build_number:
type: integer
approximate_connection_time:
type: integer
reaction:
type: object
properties:
envelope_id:
type: string
description: ""
payload:
type: object
description : ""
properties:
token:
type: string
description: ""
team_id:
type: string
description: ""
event:
type: object
properties:
user:
type: string
description: User ID who performed this event
reaction:
type: string
description: The only reaction that we need is a heart emoji
const: "heart"
item_user:
type: string
description: User ID that created the original item that has been reacted to
item:
type: object
properties:
channel:
type: string
description: Channel information of original message
ts:
type: string
description: Timestamp information of original message
event_ts:
type: string
description: Reaction timestamp
type:
type: string
description: ""
accepts_response_payload:
type: boolean
description: ""
retry_attempt:
type: integer
description: ""
retry_reason:
type: string
description: ""
acknowledge:
type: object
properties:
envelope_id:
type: string
description: ""
payload:
type: object
description: ""
```

## Define channels
Since the `Heart-Counter` makes use of just one WebSocket channel for communication you can define the `root` channel. However, this one channel can accomodate different types of message, so it is important to utilize the `messages` attribute to provide a key value pair of all the message types the application can expect which in our example is the `hello`, `reaction` and `acknowledge` message.

You can refer the [last]() tutorial on how the `bindings` attribute is used here to represent the generated WebSocket URL.

```
channels:
root:
address: /
messages:
hello:
$ref: '#/components/messages/hello'
reaction:
$ref: '#/components/messages/reaction'
acknowledge:
$ref: '#/components/messages/acknowledge'
bindings:
ws:
query:
type: object
description: Tokens are produced in the WebSocket URL generated from the [apps.connections.open](https://api.slack.com/methods/apps.connections.open) method from Slack's API
properties:
ticket:
type: string
description: Temporary token generated when connection is initiated
const: '13748dac-b866-4ea7-b98e-4fb7895c0a7f'
app_id:
type: string
description: Unique identifier assigned to the Slack app
const: 'fe684dfa62159c6ac646beeac31c8f4ef415e4f39c626c2dbd1530e3a690892f'
```

## Define operations

That brings to the important part of the tutorial where we get to finally represent the request reply pattern. The `operations` attribute represents how `Heart-Counter` will interact with Slack.

Both `helloListener` and `reactionListener` operations are set to `receive` events. However,in the case of `reactionListener` we also want to represent the message that is sent back to the server. This is where the `reply` attribute comes into play. Since both the request and reply function happens over the same WebSocket URL, both the `channel` values stay the same. However, we can differentiate the message each operation conveys by specifying the message it will send or receive.
Thus, we can say that for a `reaction` message received over the `root` channel, the `reactionListener` operation will reply with the `acknowledge` message over the same channel.

```
operations:
helloListener:
action: receive
channel:
$ref: '#/channels/root'
messages:
- $ref: '#/channels/root/messages/hello'
reactionListener:
action: receive
channel:
$ref: '#/channels/root'
messages:
- $ref: '#/channels/root/messages/reaction'
reply:
messages:
- $ref: '#/channels/root/messages/acknowledge'
channel:
$ref: '#/channels/root'
```



Putting all this together, we have our AsyncAPI document ready to go!
```
```

## Summary
You made it to the end! In this tutorial you learnt how to create an AsyncAPI document for a use case that implemented the Request Reply messaging pattern.

0 comments on commit 79d8051

Please sign in to comment.