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」、それ以外の場合は「"normal"
」を含むroutingKey
フィールドを追加する式を使用します。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 リストを 1 つの 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)