SAP Event Mesh入門

# はじめに

この記事は [chillSAP 夏の自由研究2021](https://note.com/chillsap/n/n08ef5ec73b4c) の記事として執筆しています。
この記事では以下について説明します。

1. Event Meshの概要
1. APIを使用してEvent Meshを操作する方法

# 1. Event Meshの概要
[Event Mesh](https://help.sap.com/viewer/bf82e6b26456494cbdd197057c09979f/Cloud/en-US/df532e8735eb4322b00bfc7e42f84e8d.html)はSAP BTPのサービスで、イベントを介してアプリケーションやサービスをつなぐハブの役割を果たします。

- 送信側（イベントが発生する側）では、特定のイベントが発生したときにEvent Meshにメッセージを送信する
- 受信側（イベントが発生したときに通知してほしい側）は、Event Meshのキューを通じてメッセージを受け取る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/6947e199-e59f-800b-f069-ffb74fadcbec.png)
図：[SAP Event Mesh / Concepts](https://help.sap.com/viewer/bf82e6b26456494cbdd197057c09979f/Cloud/en-US/e87534178ff448139efa93d639f13299.html)より引用


## 1.1. Event Meshの利点と用途
### Event Meshの利点
Event Meshの利点は、送信側と受信側の連携が非同期に行えるところだと思います。送信側が受信側のサービスを直接呼び出す構成だと、受信側のサービスがダウンしていると、送信側もエラーになってしまいます。Event Meshをかませることで、受信側の状況にかかわらず送信側は処理を続けることができます。

### Event Meshの用途
S/4HANAでは、標準トランザクション（たとえばBPマスタ更新や受注伝票更新）をトリガにEvent Meshにメッセージを送ることができます。イベントを受けてBTP上のアプリケーションで何らかの処理をすることができます。
参考：[SAP Enterprise Messaging for S/4HANA On-premises](https://blogs.sap.com/2019/09/16/sap-enterprise-messaging-for-s4hana-on-premises/)

Workflowで承認完了したタイミングでEvent Meshにメッセージを送り、後続処理を起動するといった使い方もできます。
参考：[Sending messages to SAP Event Mesh from a workflow](https://blogs.sap.com/2021/07/20/sending-messages-to-sap-event-mesh-from-a-workflow/)

## 1.2. 重要なコンセプト
Event Meshを利用する上で知っておく必要があるコンセプトについて説明します。
### キュー
キューはメッセージをためる「入れ物」で、受信側のアプリケーションと1:1でひもづきます。キューからメッセージを取り出すと当然ながらキューは空になるため、一つのキューを利用できるアプリケーションは一つ、というのが私の認識です。（上の図ではApp1、App2と2つのアプリが紐づいていますが、そんなことができるのかは疑問）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c793ab35-987d-de4d-423d-539a2ccc2afd.png)

### トピック
送信側のアプリケーションは、キューに直接メッセージを投げることも、トピックに対して投げることもできます。キューが「入れ物」だとするとトピックは「点」のイメージです。トピックにはキューが紐づいており、トピックに投げたメッセージはキューに送られ、各キューに紐づくアプリケーションによって消費されます。トピックを使うと、一つのイベントを起点に複数のキューにメッセージを投げることができます。一つのキューを複数のトピックに紐づけることもできます。
トピックにキューを紐づけることを「キューサブスクリプション」と呼びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/cd7d9a6d-9541-2f1c-c2cc-7fbf4e066bca.png)

## 1.3. Event MeshとEnterprise Messaging
Event MeshはかつてEnterprise Messagingという名前でしたが、今年の2月に[名称変更されました](https://blogs.sap.com/2021/02/22/please-welcome-sap-event-mesh-new-name-for-sap-cloud-platform-enterprise-messaging/)。コンセプトは変わっていないのですが、アプリケーションの画面が変わりました。以前は画面からキューを作ったりキューサブスクリプションを作ったりできたのですが、今は[API](https://help.sap.com/doc/75c9efd00fc14183abc4c613490c53f4/Cloud/en-US/rest-management-messaging.html)を使って作成する必要があるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/e50f12b3-ce3d-f6b2-df48-082ef6d7568c.png)

## 1.4. Event Meshのトライアル版
BTPのトライアルアカウントで使用できるのは、Event Meshのdevプランです。devプランが通常プランと異なるのは、**インスタンスを作成する際にnamespaceが設定できない**という点です。

通常プランだとインスタンスを作成する際に、以下のように`namespace`を指定し、キュー名やトピック名の頭にもnamespaceをつけるルールとなっています。

```json
{
    "emname": "<yourmessageclientname>",
    "namespace": "<yourorgname>/<yourmessageclientname>/<uniqueID>",
    "version": "1.1.0",
    "options": {
        "management": true,
        "messagingrest": true,
        "messaging": true
    },
	"rules": {
        "queueRules": {
            "publishFilter": [
                "${namespace}/*"
            ],
            "subscribeFilter": [
                "${namespace}/*"
            ]
        },
        "topicRules": {
            "publishFilter": [
                "${namespace}/*"
            ],
            "subscribeFilter": [
                "${namespace}/*"
            ]
        }
    },
    "resources" : {
    "units" : "10" //default value is 10//
  }
}
```
一方、トライアル版だと`namespace`の指定がなく、キュー名、トピック名のルールもありません。

```json
{
    "emname": "<yourmessagingclient>",
    "options": {
        "management": true,
        "messagingrest": true,
        "messaging": true
    }
}
```

# 2. APIを使用してEvent Meshを操作する
Event MeshのAPIには、キューやキューサブスクリプションを管理するためのManagement APIと、メッセージを送受信するためのMessaging APIがあります。なぜかAPI Business Hubに載っていないので、以下のリンク先をブックマークしておくとよいと思います。

- [Management API](https://help.sap.com/doc/75c9efd00fc14183abc4c613490c53f4/Cloud/en-US/rest-management-messaging.html)
- [Messaging API](https://help.sap.com/doc/3dfdf81b17b744ea921ce7ad464d1bd7/Cloud/en-US/messagingrest-api-spec.html)

ここでは、APIを利用して以下の操作を行います。

- キューを作成
- キューサブスクリプションを作成
- トピックにメッセージを送信
- キューからメッセージを取得

## 2.1. Event Meshのインスタンスを作成
Service MarketplaceからEvent Meshを選択し、"Create"をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/73eeb2d9-ddb6-3433-134e-b7ecdef29333.png)
Planに"dev"を指定し、インスタンス名は適当に付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/256a3dca-3dff-fe4f-1c4a-99d02e95b0a0.png)
パラメータを以下のように指定します。

```json
{
    "emname": "<yourmessagingclient>",
    "options": {
        "management": true,
        "messagingrest": true,
        "messaging": true
    }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/994a0c24-8bdd-8090-66e4-53204e504188.png)

## 2.2. サービスキーを作成
登録されたサービスインスタンスを選択してサービスキーを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c92b8658-0f40-766a-eb65-933877cf2dcb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/f16dafaa-d053-5264-f76d-3f6fc6a95a54.png)
登録されたサービスキーの中身は以下のようになっています。
managementセクションにある情報はManagement APIを呼ぶため、messagingセクションの情報はMessaging APIを呼ぶために使います。messagingセクションには3つのかたまりがありますが、これらはEvent Meshで利用可能な3つのメッセージプロトコル（AMQP, MQTT, REST API）に対応しています。

```json
{
    "xsappname": "clone-xbem-service-broker-bbfc4d3b10754cd8ba9f9722389b3269-clone!b54386|xbem-service-broker-!b2436",
    "management": [
        {
            "oa2": {
                "clientid": "xxxx",
                "clientsecret": "xxxx",
                "tokenendpoint": "https://b736177ctrial.authentication.eu10.hana.ondemand.com/oauth/token",
                "granttype": "client_credentials"
            },
            "uri": "https://enterprise-messaging-hub-backend.cfapps.eu10.hana.ondemand.com"
        }
    ],
    "messaging": [
        {
            "oa2": {
                "clientid": "xxxx",
                "clientsecret": "xxxx",
                "tokenendpoint": "https://b736177ctrial.authentication.eu10.hana.ondemand.com/oauth/token",
                "granttype": "client_credentials"
            },
            "protocol": [
                "amqp10ws"
            ],
            "broker": {
                "type": "sapmgw"
            },
            "uri": "wss://enterprise-messaging-messaging-gateway.cfapps.eu10.hana.ondemand.com/protocols/amqp10ws"
        },
        {
            "oa2": {
                "clientid": "xxxx",
                "clientsecret": "xxxx",
                "tokenendpoint": "https://b736177ctrial.authentication.eu10.hana.ondemand.com/oauth/token",
                "granttype": "client_credentials"
            },
            "protocol": [
                "mqtt311ws"
            ],
            "broker": {
                "type": "sapmgw"
            },
            "uri": "wss://enterprise-messaging-messaging-gateway.cfapps.eu10.hana.ondemand.com/protocols/mqtt311ws"
        },
        {
            "oa2": {
                "clientid": "xxxx",
                "clientsecret": "xxxx",
                "tokenendpoint": "https://b736177ctrial.authentication.eu10.hana.ondemand.com/oauth/token",
                "granttype": "client_credentials"
            },
            "protocol": [
                "httprest"
            ],
            "broker": {
                "type": "saprestmgw"
            },
            "uri": "https://enterprise-messaging-pubsub.cfapps.eu10.hana.ondemand.com"
        }
    ],
    "serviceinstanceid": "bbfc4d3b-1075-4cd8-ba9f-9722389b3269"
}
```

## 2.3. Management APIを使用する
Management APIを使用してキュー、およびキューサブスクリプションを作成します。以下ではPostmanを使用しますが、事前に"Authorization"タブで以下を設定しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/8e8d0881-7da4-999c-cb28-99f338dee9fc.png)

|項目  |設定値  |
|---|---|
|Type  |OAuth 2.0  |
|Grant Type  |Client Credentials  |
|Access Token URL  |サービスキーのmanagement.oa2.tokenendpoint  |
|Client ID  |サービスキーのmanagement.oa2.clientid  |
|Client Secret  |サービスキーのmanagement.oa2.clientsecret  |

上記項目を設定したら、"Get New Access Token"をクリックしてトークンを取得します。

### 2.3.1. キューを作成
以下のリクエストを発行し、キューを作成します。Management APIでは、登録、更新はいずれもPUTメソッドを使います。

|項目  |設定値  |
|---|---|
|メソッド  |PUT |
|URL  |サービスキーのmanagement.uri + `/hub/rest/api/v1/management/messaging/queues/<キュー名>`  |
URLの例：`https://enterprise-messaging-hub-backend.cfapps.eu10.hana.ondemand.com/hub/rest/api/v1/management/messaging/queues/q1`

レスポンス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/0aca9d65-7f70-44b3-1991-fe720f053f2f.png)

### 2.3.2. キューサブスクリプションを作成
上で作成したキューをトピックに紐づけます。

|項目  |設定値  |
|---|---|
|メソッド  |PUT |
|URL  |サービスキーのmanagement.uri + `/hub/rest/api/v1/management/messaging/queues/<キュー名>/subscriptions/<トピック名>`  |
URLの例：`https://enterprise-messaging-hub-backend.cfapps.eu10.hana.ondemand.com/hub/rest/api/v1/management/messaging/queues/q1/subscriptions/topic1`

レスポンス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c1e04efb-938d-0b0d-5baa-41d4e6bd83d3.png)

## 2.4. Messaging APIを使用する
続いてMessaging APIを使ってメッセージを送受信してみます。"Authorization"タブに指定する認証情報は、Management APIと同じです。

### 2.4.1. トピックにメッセージを送信
上で作成したトピックにメッセージを送信します。ボディはどのような形式でも構いませんが、ここではJSON形式にします。URLはサービスキーのmessagingセクションの3つ目の、protocolが`httprest`となっているかたまりの`uri`を使用します。

|項目  |設定値  |
|---|---|
|メソッド  |POST |
|URL  |サービスキーのmessaging[2].uri + `/messagingrest/v1/topics/<トピック名>/messages`  |
|ヘッダ: Content-Type  |application/json|
|ヘッダ: x-qos(※)  |0|
※Quality of Serviceを表す項目。値は0または1。

- 0: 受信確認を必要とせず、キューから受信側へ1回送信したらメッセージを削除する
- 1: 受信確認がされるまでキューから受信側へメッセージを再送する

参考：https://help.sap.com/viewer/bf82e6b26456494cbdd197057c09979f/Cloud/en-US/3f424ff1ae3b4bc084c4f1ea0be96f54.html

URLの例：`https://enterprise-messaging-pubsub.cfapps.eu10.hana.ondemand.com/messagingrest/v1/topics/topic1/messages`

ボディ

```json
{
    "message": "hello!",
    "anyProperty": "12345"
}
```

レスポンス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c4bf369a-be5d-e4db-e62a-02718dea804a.png)

### 2.4.2. キューからメッセージを取得
キューからメッセージを取得する方法にはAPIを使う、[Webhookサブスクリプションを使う](https://help.sap.com/viewer/bf82e6b26456494cbdd197057c09979f/Cloud/en-US/387a7e03feb24f6bb154ab1170e45eee.html?q=webhook)、CAPを使うなどがありますが、ここではAPIを使う方法を紹介します。APIを使う方法は「自らメッセージを取りに行く」プル型の取り方です。これに対してWebhookやCAPは「イベントが発生したらメッセージを受け取る」プッシュ型の取り方です。

注目すべきは「メッセージを取得する」アクションなのにメソッドがPOSTになっている点です。メッセージは取得（消費）されるとキューから削除されるため、実際はキューの更新を伴う操作だからです。

|項目  |設定値  |
|---|---|
|メソッド  |POST |
|URL  |サービスキーのmessaging[2].uri + `/messagingrest/v1/queues/<キュー名>/messages/consumption`  |
|ヘッダ: x-qos  |0|
URLの例：`https://enterprise-messaging-pubsub.cfapps.eu10.hana.ondemand.com/messagingrest/v1/queues/q1/messages/consumption`

レスポンス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/f1f3f8f4-804a-ccf4-8e0d-6e18c62afa16.png)
もう一度同じリクエストを実行すると、レスポンスは空になります。キューからは先入れ先出しでメッセージが一つずつ取れます。最後のメッセージが取り出されると、それ以降は空のレスポンスが返ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/cb528755-45a2-7c42-1794-5d17d18a475e.png)

次回はCAPを使ってメッセージを送受信する方法について紹介します。

# 参考
Event Meshのコンセプトについて理解するには、DJ Adams氏の以下の動画がおすすめです。

- [Diving into messaging on SAP Cloud Platform - Part 1 - Setup and overview](https://www.youtube.com/watch?v=qHjzefGn4W4&list=PL6RpkC85SLQATt7rdOTDUYRhtE4TQ7gG8)