1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SAP Cloud Integration】Batchリクエストで複数エンティティを送信する

Last updated at Posted at 2021-11-26

はじめに

この記事では、Cloud Integrationを使いODataのバッチリクエストを投げる方法について、複数の方法を試した結果をまとめます。

シナリオ例

2つシステムAとBがあり、システムAに登録された発注(PO)をシステムBに受注(SO)として登録したいとします。システムAには発注を取得するためのAPIがあり、システムBには受注を登録するためのOData APIがあります。

そこで、Cloud Integrationを使い以下を行います。

  1. システムAのAPIを呼び発注を取得する
  2. システムAの発注とシステムBの受注の項目をマッピングする
  3. システムBのAPIを呼び受注を登録する

image.png

システムAからの発注取得はリアルタイムではなく、定期的なバッチ処理として行います。そのため、複数のデータを一度に取得してシステムBに登録する必要があります。

Cloud IntegrationでBatchクエストを投げる

ODataで複数のデータを一度に投げる場合、Batchクエストを使います。Cloud IntegrationのOData Adapterを使うとBatchリクエストを投げることができます。また、HTTP Adapterを使ってもBatchリクエストを投げることができます(この場合、Groovy Scriptでペイロードの編集が必要)。

検証

「シナリオ例」の構成に似た形にするため、以下のサービスを使います。本来は発注-受注でやりたいところですが、マッピングを簡単にするため受注-受注としています。

CAPのODataサービスのソースは以下に格納しています。
https://github.com/miyasuta/api-sales-orders-mock

検証パターン

  1. HTTPアダプター + Groovy Script
  2. OData V2アダプター
  3. OData V4アダプター

結果

試してみたところ、うまくいった方法とうまくいかなかった方法がありました。以下が今のところの結果です。OData V4アダプターを使って、今回やりたいシナリオは実現できませんでした。
※私の設定が間違っている可能性もありますので、ご参考程度に
image.png

検証内容

1. HTTPアダプター + Groovy Script

インテグレーションフロー

インテグレーションフローは以下のようになります。
image.png

①HTTPS Sender Adapter

HTTPリクエストでインテグレーションフローを呼べるように、HTTPS Sender Adapterを使用します。
image.png

②Set Header [Content Modifier]

SAP GraphのAPIを呼ぶ際に使用するヘッダの設定をします。
※API Business HubでapiKeyを確認する方法はこちら
image.png

③Get Graph SO [OData V4 Receiver Adapter]

SAP GraphのSalesOrder APIを呼び、データを取得します。
image.png
複数件取得してバッチリクエストに渡すため、Query Optionsに$top=2を指定しています。また、ヘッダに②で設定したapiKeyを設定しています。
image.png

④Message Mapping

SAP Graphで取得したSalesOrderをCAPのODataサービスの項目にマッピングします。マッピングにはメタデータのedmxファイルを使用することもできますが、edmxファイルベースだとヘッダ+明細のようなディープな構造にマッピングすることができません。そこで、以下のブログを参考にディープな構造を持つxsdファイルを作成しておきます。
Multiple Entities Mapping in OData Service in Cloud Platform Integration.

OData V2 Receiver Adapter(仮)を作成
これはxsdファイルを作成するためだけのものなので、後で削除します。Selectをクリックして詳細を設定します。
image.png
Sub Levelsに1を設定すると、to_Item(ナビゲーション先の項目)にもチェックを入れられます。Generate XML Schema Definitionにチェックを入れるとxsdファイルが生成されます。このファイルはマッピングの中で参照することができます。
image.png

Message Mapping
ターゲットに前のステップで生成したxsdファイルを選択します。
image.png
image.png
マッピングは以下のようになります。
image.png
SAP GraphのAPIには明細データがないので、明細は固定値で2件設定します。
image.png

⑤XML to JSON Converter

Message Mappingの結果はXML形式になるので、それをJSON形式に変換します。以下の設定はデフォルトのままです。
image.png

⑥Create Batch Payload [Groovy Script]

前ステップでのマッピングの後、ペイロードは以下のような形になっています。

before
{
	"A_SalesOrder": {
		"A_SalesOrder": [{
			"SalesOrder": "9000000000",
			"to_Item": {
				"A_SalesOrderItem": [{
					"SalesOrderItem": "10",
					"Material": "ABC"
				}, {
					"SalesOrderItem": "20",
					"Material": "DEF"
				}]
			},
			"SalesOrderType": "OR",
			"SalesOrganization": "1100",
			"DistributionChannel": "01",
			"SoldToParty": "10004"
		}, {
			"SalesOrder": "9000000010",
			"to_Item": {
				"A_SalesOrderItem": [{
					"SalesOrderItem": "10",
					"Material": "ABC"
				}, {
					"SalesOrderItem": "20",
					"Material": "DEF"
				}]
			},
			"SalesOrderType": "OR",
			"SalesOrganization": "1100",
			"DistributionChannel": "01",
			"SoldToParty": "10332"
		}]
	}
}

それを、JSON形式のBatchリクエスト(以下の形式)になるようにGroovy scriptで編集します。
参考:https://docs.microsoft.com/en-us/odata/client/batch-operations#json-batch

{
	"requests": [{
		"id": "1",
		"method": "POST",
		"url": "/A_SalesOrder",
		"headers": {
			"content-type": "application/json"
		},
		"body": {
			"SalesOrderType": "OR",
			"SalesOrganization": "1100",
			"DistributionChannel": "01",
			"SoldToParty": "10004",
			"PurchaseOrderByCustomer": "",
			"to_Item": [{
				"SalesOrderItem": "10",
				"Material": "ABC"
			},{
				"SalesOrderItem": "20",
				"Material": "DEF"
			}]
		}
	}, {
		"id": "2",
		"method": "POST",
		"url": "/A_SalesOrder",
		"headers": {
			"content-type": "application/json"
		},
		"body": {
			"SalesOrderType": "OR",
			"SalesOrganization": "1100",
			"DistributionChannel": "01",
			"SoldToParty": "10004",
			"PurchaseOrderByCustomer": "",
			"to_Item": [{
				"SalesOrderItem": "10",
				"Material": "ABC"
			},{
				"SalesOrderItem": "20",
				"Material": "DEF"
			}]
		}
	}]
}

Groovy script

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
    def stringBody = message.getBody(java.lang.String) as String;
    def slurper = new groovy.json.JsonSlurper();
    def object = slurper.parseText(stringBody);
    def json = new groovy.json.JsonBuilder();
    def orders = json(object).A_SalesOrder.A_SalesOrder;
    def requests = [];
    def id = 0;
    
    orders.each() {
        id += 1;
        def items = it.to_Item.A_SalesOrderItem;
        def formattedItems = [];
        
        items.each() {
            def item = $/{
                "SalesOrderItem": "${it.SalesOrderItem}",
                "Material": "${it.Material}"
            }/$;
            formattedItems.push(item);
        }
        
        def obj = $/{
            "id": "${id}",
            "method": "POST",
            "url": "/A_SalesOrder",
            "headers": {
                "content-type": "application/json"
            },
            "body": {
                "SalesOrderType": "${it.SalesOrderType}",
                "SalesOrganization": "${it.SalesOrganization}",
                "DistributionChannel": "${it.DistributionChannel}",
                "SoldToParty": "${it.SoldToParty}",
                "PurchaseOrderByCustomer": "${it.PurchaseOrderByCustomer}",
                "to_Item": ${formattedItems}
            }        
        }/$;
        requests.push(obj);
    }
    
    def newBody = $/{
        "requests": ${requests}
    }/$.toString();
    
    message.setBody(newBody);
    return message;
}
⑦Post SO [HTTP Receiver Adapter]

OData V2の宛先にBatchリクエストを送信します。ヘッダに②で設定したcontent-typeを設定しています。
image.png

テスト

インテグレーションフローをデプロイし、Postmanからテストします。複数のエンティティを登録することができました。
なお、⑦の宛先をOData V4に変えても正常に動作します。(Batchのペイロードの形式はV2とV4で変わらないため)
image.png

2. OData V2アダプター

インテグレーションフロー

インテグレーションフローは以下のようになります。前半部分は1.と一緒なので、変わる部分だけ紹介します。番号はフロー上で定義する順番で振っています。
image.png

①Post SO [OData V2 Receiver Adapter]

OData V2の宛先にリクエストを投げます。HTTP Adapterと違い、アドレスに/$batchは含みません。
image.png
Batchリクエストを投げるため、Enable Batch Processingにチェックを入れます。Content TypeはデフォルトではAtomになっていますが、CAPはBatchリクエストでAtomをサポートしていないためJSONとします。ヘッダに関しては、OData Adapterが自動的に設定してくれるので何も設定しません。
image.png
ここでも、ディープな構造で登録するためSub Levelsに1を設定します。マッピングで使うxsdファイルを生成するため、Generate XML Schema Definitionにチェックを入れます。
image.png

②Message Mapping

①で生成したxsdファイルを使ってマッピングをします。Batchリクエスト用の構造は以下の形になっています。

<batchParts>
	<batchChangeSet>
		<batchChangeSetPart>
			<method>POST</method>
			<uri></uri>
			<A_SalesOrder>...</A_SalesOrder>
		</batchChangeSetPart>
	</batchChangeSet>
	<batchChangeSet>
		<batchChangeSetPart>
			<method>POST</method>
			<uri></uri>
			<A_SalesOrder>...</A_SalesOrder>
		</batchChangeSetPart>
	</batchChangeSet>
</batchParts>

image.png
明細は固定値で設定します。
image.png

③Content Modifier

Exchange PropertyのSAP_BatchLineSeparatorCRLFを設定します。この設定をしない場合、OData側から"Boundary expected at position 0"というエラーが返ってきました。
参考:https://blogs.sap.com/2017/05/10/batch-operation-in-odata-v2-adapter-in-sap-cloud-platform-integration/#comment-437633
image.png

テスト

インテグレーションフローをデプロイし、Postmanからテストします。レスポンスの形式がxmlになっていますが、OData AdapterのヘッダにAccept: application/jsonを設定しても変わりませんでした。レスポンス形式の指定はOData Adapterが行っていると考えられます。
image.png

3. OData V4アダプター

OData V4アダプターでは、任意の数のリクエストをディープな構造で投げることができませんでした。

問題点

以下では、何が問題だったかについて説明します。

①ディープな構造のxsdファイルを作れない

OData V4アダプターでBatchリクエストを投げるための設定は以下のようになります。更新系のリクエストの場合、"Add Changeset"ボタンを押してChangesetを追加します。1つのChangesetにつきエンティティは1つしか選択できず、"Sub Levels"もグレーアウトされています。
image.png

ODataアダプターの設定は以下のようになります。
image.png
image.png

②任意の数のリクエストを投げられない

メッセージマッピングでシミュレーションをすると、複数リクエストがある場合にbatchChangeSet1が複数できます。
image.png
この1という数字がリクエストIDになるのですが、本来一意にならなければならないところ、全部1なのでエラーになります。
image.png
xsdファイルを作る段階でChangesetを複数追加すればbatchChangeSet2, batchChangeSet3...となりますが、それだとあらかじめ決まった数のリクエストしか投げられません。登録する件数がわからない場合はこの方法は使えないと思われます。

③Deserialization Errorになる

1件のリクエストならどうかということで、GraphのSalesOrderを取ってくるときに$top=1を設定してみます。
image.png

フロー自体は正常終了するものの、OData側でエラーになっていました。
Deserialization Error: The value of 'SalesOrderType@odata.type' must describe correctly the type 'Edm.String'.","@Core.ContentID":"1
image.png

BatchリクエストでないときはOData V4アダプターでも正常に登録できたので、このエラーは謎です。

まとめ

OData V2の場合、OData V2アダプターを使うのが最も簡単な方法です。
OData V4で任意の数のBatchリクエストを投げたい場合は、今のところHTTPアダプターを使うしかないのではないかと考えています。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?