CEL を使用して変換式を記述することで、CloudEvents データを変換できます。詳細については、受信したイベントを変換するをご覧ください。
以下に、イベントデータを変換する CEL 式の記述方法を示す一般的なユースケースと例をいくつか示します。
標準的なユースケース
イベント データを変換する際の標準的なユースケースを以下に示します。
データの正規化
ダウンストリーム サービスで処理しやすくするために、イベント メッセージ内のネストされたデータ構造をフラット化する必要があります。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "orderId": "12345", "customer": { "firstName": "Alex", "lastName": "Taylor", "address": { "street": "1800 Amphibious Blvd.", "city": "Mountain View" } } } }
次の出力が得られる CEL 式を作成します。
{ "data": { "orderId": "12345", "customerFirstName": "Alex", "customerLastName": "Taylor", "customerStreet": "1800 Amphibious Blvd.", "customerCity": "Mountain View" } }
- 解決方法 1:
出力データを手動でフォーマットします。これにより、フィールド名を一覧表示し、出力に必要な要素のみを選択できます。これは、入力が予測可能で、フィールド数が少ない場合に妥当なアプローチです。
setField
関数は、指定されたキーを持つイベントのフィールドを追加または置き換えます。次に例を示します。message.setField("data", { "orderId": message.data.orderId, "customerFirstName": message.data.customer.firstName, "customerLastName": message.data.customer.lastName, "customerStreet": message.data.customer.address.street, "customerCity": message.data.customer.address.city, })
- 解決方法 2:
式で関数を使用します。
setField
関数は、指定されたキーを持つイベントのフィールドを追加または置き換えます。denormalize
関数は、深い構造を Key-Value ペアのリストにフラット化します。フィールド名は、ピリオド(.
)を使用して区切られ、構造階層がセグメント化されます。次に例を示します。message.setField("data", message.data.denormalize())
これにより、予想されるペイロードとは若干異なる次の出力が得られます。ただし、この方法には、任意の入力で動作し、任意の数の受信フィールドを自動的に含める短い CEL 式を使用できるという利点があります。
{ "data": { "orderId": "12345", "customer.firstName": "Alex", "customer.lastName": "Taylor", "customer.address.street": "1800 Amphibious Blvd.", "customer.address.city": "Mountain View" } }
データ マスキング
イベント ペイロード内の機密データを、セキュリティの低い環境に送信する前にマスクする必要があります。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "userId": "user123", "email": "alex@example.com", "creditCardNumber": "1234-5678-9012-3456" } }
次の出力が得られる CEL 式を作成します。
{ "data": { "userId": "user123", "email": "a***@example.com", "creditCardNumber": "xxxx-xxxx-xxxx-3456" } }
- 解決方法:
式を使用して、メールアドレスやクレジット カード番号などの機密情報をマスクします。
setField
関数は、指定されたキーを持つイベントのフィールドを追加または置き換えます。extract
正規表現関数は RE2 構文に従います。次に例を示します。message .setField("data.email", re.extract(message.data.email, "(^.).*@(.*)", "\\1***@\\2")) .setField("data.creditCardNumber", re.extract(message.data.creditCardNumber, "(\\d{4})\\D*$", "xxxx-xxxx-xxxx-\\1"))
データの除去
特定の条件に基づいて、イベント ペイロードから特定のフィールドを削除する必要があります。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "orderId": "12345", "customerType": "gold", "discountCode": "VIP" } }
次の出力が得られる CEL 式を作成します。
{ { "orderId": "12345", "customerType": "gold" } }
- 解決方法:
customerType
が「gold」の場合にdiscountCode
フィールドを編集する式を使用します。removeFields
関数は、イベントから特定のフィールドを削除します。次に例を示します。message.data.customerType == "gold" ? message.removeFields(["data.discountCode"]) : message
データ変換
ある形式または型から別の形式または型にデータを変換する必要がある。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "orderDate": "2024-10-31T12:00:00Z", "totalAmount": "1500" } }
次の出力が得られる CEL 式を作成します。
{ "data": { "orderDate": 1704086400, "totalAmount": 1500.00 } }
- 解決方法:
orderDate
を UNIX タイムスタンプに変換する式と、totalAmount
型をstring
からdouble
(浮動小数点数)に変換する式を使用します。setField
関数は、指定されたキーを持つイベントのフィールドを追加または置き換えます。文字列操作関数を使用して、文字列の結果を変換できます。次に例を示します。message .setField("data.orderDate", int(timestamp(message.data.orderDate))) .setField("data.totalAmount", double(message.data.totalAmount))
条件付きルーティング
イベントデータに基づいて、イベントをさまざまな宛先に転送する必要があります。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "eventType": "order.created", "orderValue": 200 } }
次の出力が得られる CEL 式を作成します。
{ "data": { "eventType": "order.created", "orderValue": 200, "routingKey": "highValue" } }
- 解決方法:
orderValue
が 100 より大きい場合は「highValue」を含むroutingKey
フィールドを追加し、それ以外の場合は"normal"
を追加する式を使用します。routingKey
フィールドを使用して、ルーティング パスを特定できます。setField
関数は、指定されたキーを持つイベントのフィールドを追加または置き換えます。次に例を示します。message.data.orderValue > 100 ? message.setField("data.routingKey", "highValue") : message.setField("data.routingKey", "normal")
デフォルト値の処理
イベント ペイロードに特定のフィールドが存在しない場合は、デフォルト値が設定されるようにする必要があります。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "itemName": "Product A" } }
次の出力が得られる CEL 式を作成します。
{ "data": { "itemName": "Product A", "quantity": 1 } }
- 解決方法:
フィールドがまだ存在しない場合は、デフォルト値
1
のquantity
フィールドを追加する式を使用します。has
マクロは、フィールドが使用可能かどうかをテストします。setField
関数は、指定されたキーを持つイベントのフィールドを追加または置き換えます。次に例を示します。has(message.data.quantity) ? message : message.setField("data.quantity", 1)
文字列操作
イベントデータの文字列フィールドの一部を抽出または変更する必要がある。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "customerEmail": "alex@example.com" } }
次の出力が得られる CEL 式を作成します。
{ "data": { "customerEmail": "alex@example.com", "emailDomain": "example.com" } }
- 解決方法:
customerEmail
フィールドからドメイン名(「example.com」)を抽出し、新しいemailDomain
フィールドに保存する式を使用します。setField
関数は、指定されたキーを持つイベントのフィールドを追加または置き換えます。extract
正規表現関数は RE2 構文に従います。次に例を示します。message .setField("data.emailDomain", re.extract(message.data.customerEmail, "(^.*@)(.*)", "\\2"))
リストとマップのオペレーション
イベントデータでリストまたはマップを操作する必要がある。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "productIds": [ "product123", "product456" ] } }
次の出力が得られる CEL 式を作成します。
{ "data": { "productIds": [ "product123", "product456" ], "productFound": true } }
- 解決方法:
productIds
リストに「product456」が存在するかどうかを確認し、結果(true
またはfalse
)を新しいproductFound
フィールドに保存する式を使用します。setField
関数は、指定されたキーを持つイベントのフィールドを追加または置き換えます。exists
マクロは、述語がリストのすべての要素を保持しているかどうかをテストし、結果を「or」演算子で結合します。次に例を示します。message.setField("data.productFound", message.data.productIds.exists(id, id == "product123"))
エラー処理
イベント ペイロードで発生する可能性のあるエラーや予期しないデータを適切に処理する必要があります。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "quantity": "abc" } }
次の出力が得られる CEL 式を作成します。
{ "data": { "quantity": 0, "error": "Invalid quantity" } }
- 解決方法:
quantity
フィールドを整数に変換しようとする式を使用します。変換が失敗した場合は、quantity
フィールドを0
に設定し、値が「Invalid quantity」の新しいerror
フィールドを追加します。has
マクロは、フィールドが使用可能かどうかをテストします。type
関数は、値の型を返します。matches
正規表現関数は RE2 構文に従います。setField
関数は、指定されたキーを使用してイベントのフィールドを追加または置き換えます。
次に例を示します。
// Check if data.quantity exists has(message.data.quantity) && // Check if data.quantity is a string type(message.data.quantity) == string && // Check if string consists of digits message.data.quantity.matches(r'^-?[0-9]+$') ? // If data.quantity is valid, use message message : // If data.quantity is invalid, set to 0 and generate error message .setField("data.quantity", 0) .setField("data.error", "Invalid quantity")
複雑なユースケース
イベント データの変換時の複雑なユースケースを次に示します。
データの変換
ネストされたイベントデータに対して複数の変換を行う必要がある。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "orderId": "12345", "customer": { "firstName": "Alex", "lastName": "Taylor", "email": "alex@example.com", "address": { "street": "1800 Amphibious Blvd.", "city": "Mountain View", "state": "CA" } }, "items": [ { "itemId": "item1", "price": 10.00, "quantity": 2 }, { "itemId": "item2", "price": 5.00, "quantity": 1 } ] } }
次の出力が得られる CEL 式を作成します。
{ "data": { "orderId": "12345", "customer.firstName": "Alex", "customer.lastName": "Taylor", "customer.email": "a***@example.com", "customer.address.city": "Mountain View", "customer.address.state": "CA" } }
- 解決方法:
住所から市区町村と都道府県を抽出し、メールアドレスをマスクする式を使用します。
setField
関数は、指定されたキーを使用してイベントのフィールドを追加または置き換えます。toMap
関数は、CEL マップの CEL リストを単一の CEL マップに変換します。extract
正規表現関数は RE2 構文に従います。removeFields
関数は、イベントから特定のフィールドを削除します。denormalize
関数は、深い構造を Key-Value ペアのリストにフラット化します。フィールド名は、構造階層をセグメント化するためにピリオド(.
)で区切られます。
次に例を示します。
message .setField("data", message.data.setField("customer.address", message.data.customer.address.map(key, key == "city" || key == "state", { key: message.data.customer.address[key] }).toMap()) .setField("customer.email", re.extract(message.data.customer.email, "(^..?).*@(.*)", "\\1***@\\2")) .removeFields(["items"]) .denormalize() )
データの形式設定とルーティング
イベントデータの形式を設定し、商品情報を追加してから、イベント メッセージをルーティングする必要があります。
- シナリオ:
次の CloudEvents データがあるとします。
{ "data": { "productId": "p123", "productName": "Example Product", "category": "electronics" } }
次の出力が得られる CEL 式を作成します。
{ "data": { "productId": "electronics-p123", "productName": "EXAMPLE PRODUCT", "category": "electronics", "routingKey": "electronics" } }
- 解決方法:
商品名を大文字に変換し、カテゴリに基づいて商品 ID に接頭辞を追加し、ダウンストリーム処理のルーティングキーを含める式を使用します。
setField
関数は、指定されたキーを持つイベントのフィールドを追加または置き換えます。upperAscii
関数は、すべての ASCII 文字が対応する大文字に変換された文字列を返します。次に例を示します。message .setField("data.productId", message.data.category + "-" + message.data.productId) .setField("data.productName", message.data.productName.upperAscii()) .setField("data.routingKey", message.data.category)