Statement カタログ
statements 配列の各要素が 1 つの文(statement)です。このドキュメントでは 25 種の文のフィールド、動作、結果を整理します。すべての値の箇所は 値式 の規則(参照、リテラル、JsonLogic、ロケールマップ)に従います(例外は Regex の pattern と Cache の key の 2 つです。Regex と Cache 参照)。
Statement 概要
| 分類 | type | 一行要約 |
|---|---|---|
| リソース書き込み | ResourceCreate | Content/Media を生成(任意で公開) |
ResourceUpdate | Content/Media フィールドの全体置換(渡さなかった field・locale は削除) | |
ResourcePatch | Content/Media フィールドの部分マージ(指定した field・locale のみ。リテラル null は削除) | |
ResourceDelete | 削除(Draft・Archived のみ。Published の場合はまず unpublish) | |
ResourcePublish / ResourceUnpublish | 公開 / 公開取り消し | |
ResourceArchive / ResourceUnarchive | アーカイブ / アーカイブ解除 | |
| リソース読み取り | ResourceRead | id で単一件取得 |
ResourceFind | フィルターで最初にマッチした単一件(なければ null) | |
ResourceForEach | フィルターにマッチするリソースを内部的に巡回し、項目ごとに onEach を実行 | |
ResourceCount | フィルターにマッチする件数だけを数える(項目は読み取らない) | |
| 外部 | Http | 外部 HTTP 呼び出し({ status, body }) |
EmailSend | 登録済みの EmailAccount でメール 1 通を送信 | |
| 変数 | SetVar | script-scoped 変数の宣言/更新 |
| キャッシュ | Cache | その Script だけの短命なキャッシュを読み取り/書き込み/削除 |
| 値のパース | ParseJson | JSON テキストを値(オブジェクト・配列・スカラー)にパースしてバインド |
| 署名とテキスト | Signature | 受け取った署名コードが秘密鍵で作ったコードと同じかを検証(Boolean) |
Hash | 鍵なしのダイジェスト計算(文字列) | |
Regex | 正規表現の適用。一致の有無(Boolean)またはキャプチャグループ(配列) | |
| 制御フロー | If | 条件分岐 |
Loop | 繰り返し(foreach / while / counted) | |
Parallel | ブランチの同時実行 | |
Return | 結果の返却と早期終了 | |
Try | 例外処理(catch/finally) |
id で対象を指定しない Content の文は、扱う Content Type を必ず書きます。
ResourceFind・ResourceForEach・ResourceCountは、resourceが"Content"ならcontentTypeが必須です。Space 全体を横断する Content の照会はありません。ResourceCreateも作成する Content Type を書きます。Media は Space 全体で 1 組のため範囲を切らず、id で対象を指定する文(ResourceRead・ResourceUpdate・ResourcePatch・ResourceDeleteと公開・アーカイブの文)はtargetがあるため範囲が不要です。
循環呼び出しは最大 3 回まで。 上のリソース書き込み文(
ResourceCreate・ResourceUpdate・ResourcePublishなど)でpropagateEventsを有効にすると(デフォルトは無効)、変更イベントが発生し、そのイベントが Webhook を通じて再び Script を実行することがあります。このように続く連鎖(Script → イベント → Webhook → Script → …)は最大 3 回までしか続かず、それを超えると自動的に打ち切られて無限循環を防ぎます。
共通フィールド
{ "type": "<StatementType>", "name": "<任意、script 内で一意>", /* ...タイプ別フィールド... */ }type: 判別子です。上表の値のいずれかです(必須)。name: 任意です。付けると結果が/<name>としてコンテキストにバインドされ、以降の statement が{ /<name>/... }で参照します。結果を使わない場合は省略します。- バインディング名の規則:
nameはコンテキストルートにそのまま載るキーなので、保存時に検証されます。使えるのは英数字と_・-だけで(JSON Pointer のキーとして使える必要があるため、それ以外の文字や空の名前は拒否されます)、予約ルート(payload・rawPayload・headers・vars・error・now)と同じであってはならず、1 つの Script 内で一意でなければなりません。違反した場合(形式違反・予約語の使用・重複)は保存が拒否されます。
エンティティ参照の形式
contentType、target などのエンティティ参照は { "sys": { "id": <値式> } } という 1 つの形式に統一します。sys.id だけが必要で、対象タイプは resource から推論します(sys.type と sys.targetType は省略)。
contentType.sys.idは通常リテラルです(例:"ct_post")。target.sys.idは通常{ /ptr }値式です(実行時に resolve。例:{ /payload/sys/id })。
resource
リソース系の文は対象の種類を resource: "Content" | "ContentType" | "Media" | "ServiceUser" で指定します。
Content Type を受け取るのは ResourceCount だけです。他の文に書くと保存が拒否されます。様式そのものを作ったり直したりする作業は、Script ではなく CMA の担当です。
ServiceUser(製品にサインアップした会員)は読み取り専用です。読み取りの 3 文(ResourceRead・ResourceFind・ResourceForEach)だけがこの値を受け取り、書き込み文に書くと保存が拒否されます(エラー 参照)。規則は 会員ディレクトリの読み取り で扱います。
リソース書き込み
すべての書き込み文は propagateEvents(デフォルト false)を持ちます。true にすると、その書き込みが変更イベントを発生させ、Webhook のような後続の動作が実行されます。デフォルトは発生させません(サイレントなシステム書き込み)。
ResourceCreate
Content または Media を作成します。Content と Media は fields モデルを共有し、値はロケールマップです。
| フィールド | 対象 | 説明 |
|---|---|---|
resource | 共通 | "Content" または "Media"(必須) |
contentType | Content | 作成する Content Type({ sys: { id } })。Content の場合は必須 |
fields | 共通 | フィールドマップ { "<field>": { "<locale>": 値 } }。populate するフィールドごとにデフォルトロケールのバケットが必須。Content のキーは Content Type 定義に従い、Media のキーは固定(title・description・file) |
locale | 共通 | (便宜)指定すると fields の各値を { <locale>: 値 } に自動ラップ |
publish | 共通 | 書き込み後に公開(CDA/ACDA へ露出)。デフォルト true |
Mediaのfile:fields.file.{locale}の値はインジェスト指示{ "source": <値式>, "encoding": "url"|"base64" }です(どちらも必須)。ファイルを含む書き込みでは、エンジンがインジェストを実行します(urlならダウンロード、base64ならデコードしたうえでアップロードして処理します)。このインジェストは宣言する時間がないため 30 秒の基本予算から出ていき(時間予算)、外部呼び出しの上限には数えられません。ファイルのない(fileless)Media も作成できます。publish:trueでファイルがない、または処理が未完了の場合は公開段階でエラーになり、publish:falseの場合はそのままDraftです。- 結果(
nameバインディング): 作成されたリソースです。{ /<name>/sys/id }、{ /<name>/fields/<field>/<locale> }。
// Content
{ "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
"fields": { "title": { "en-US": "{ /payload/fields/title }" } }, "publish": true, "name": "post" }
// Media. file はインジェスト指示
{ "type": "ResourceCreate", "resource": "Media",
"fields": {
"title": { "en-US": "{ /payload/fields/prompt }" },
"file": { "en-US": { "source": "{ /gen/body/data/0/url }", "encoding": "url" } }
}, "name": "img" }ResourceUpdate
対象の Content や Media のフィールドを全体置換します(PUT)。fields に渡したものがそのまま新しいフィールドになり、ここにない field と locale は消去されます。一部だけ変更するには ResourcePatch を使います。
| フィールド | 説明 |
|---|---|
resource | "Content" または "Media" |
target | 対象({ sys: { id } }、必須)。id は通常 { /ptr } |
fields | 書き込むフィールド全体。値はロケールマップ。全体置換なので、ここにない field と locale は削除されます。Media file はインジェスト指示(上記 ResourceCreate 参照)。列挙したファイルは常に再インジェストし、渡さなかった locale のファイルは削除 |
locale | (便宜)fields を自動ラップ |
version | (任意)値式(Int)。楽観的ロック。指定すると対象の現在の sys.version と一致する場合のみ更新し、不一致の場合はバージョン競合エラーで abort します(Try で catch 可能)。省略時は検査なし(last-write-wins) |
publish | 更新後に republish。デフォルト true |
Media で metadata だけを変更しようと Update を使うと file が抜けてファイルがすべて削除されます(全体置換なので)。部分的な変更には必ず ResourcePatch を使います。
{ "type": "ResourceUpdate", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
"fields": { "title": { "en-US": "Hello", "ko-KR": "안녕" }, "status": { "en-US": "published" } } }ResourcePatch
対象の Content または Media のフィールドを部分マージします(PATCH)。fields に渡したフィールド(およびその中のロケール)のみを上書きし、言及しなかったフィールドとロケールはそのまま維持します。値の形式、locale、version、publish は ResourceUpdate と同じです。
| フィールド | 説明 |
|---|---|
resource | "Content" または "Media" |
target | 対象({ sys: { id } }、必須)。id は通常 { /ptr } |
fields | 上書きするフィールド。値はロケールマップ。指定したフィールドとロケールバケットのみを更新(残りは維持)。値がリテラル null の場合はその (field, locale) を削除。Media file はインジェスト指示(上記 ResourceCreate 参照) |
locale | (便宜)fields を自動ラップ |
version | (任意)ResourceUpdate と同じ(楽観的ロック) |
publish | 更新後に republish。デフォルト true |
- 特定のロケールやファイルの削除: 値にリテラル
nullを指定します。例:"title": { "fr-FR": null }(fr-FR のタイトルを削除)、"file": { "en-US": null }(en-US のファイルを削除)。値式が実行時に null と評価されるのは削除ではなくエラーです(リテラル null のみ削除)。 - Media
fileにインジェスト指示を与えると、そのロケールのファイルを置き換えます。ファイルを与えなければ維持します。
// viewCount(en-US) だけ +1。title やその他のロケールなど残りはそのまま維持
{ "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
"fields": { "viewCount": { "en-US": { "$+": [ "{ /payload/fields/viewCount }", 1 ] } } } }ResourceDelete
対象を削除します。Draft と Archived 状態のみ削除できます。Published や Changed の場合は拒否されるため、まず ResourceUnpublish する必要があります。Media はファイルを処理している最中であれば削除が拒否されます。auto-unpublish はしません(CMA/ACMA で同じ)。
| フィールド | 説明 |
|---|---|
resource | "Content" または "Media" |
target | 対象({ sys: { id } }、必須) |
{ "type": "ResourceDelete", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }ResourcePublish, ResourceUnpublish, ResourceArchive, ResourceUnarchive
対象の公開状態とアーカイブ状態を独立して制御します。4 つともフィールドは同じです。各操作の status 前提条件は CMA/ACMA と同じです。ResourcePublish は Archived からは実行できず、ファイルの処理が終わっている必要があります。ResourceUnpublish は Published・Changed からのみ、ResourceArchive は Draft からのみ、ResourceUnarchive は Archived からのみ実行できます。
| フィールド | 説明 |
|---|---|
resource | "Content" または "Media" |
target | 対象({ sys: { id } }、必須) |
version | (任意)値式(Int)。楽観的ロック。指定すると現在の sys.version と一致する場合のみ実行 |
{ "type": "ResourcePublish", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }
{ "type": "ResourceUnpublish", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }
{ "type": "ResourceArchive", "resource": "Media", "target": { "sys": { "id": "{ /m/sys/id }" } } }リソース読み取り
ResourceRead・ResourceFind はリソースを読み取って値にバインドし、ResourceCount は件数だけを数えます。3 つとも状態を変更しません(propagateEvents はありません)。ResourceForEach も照会自体は読み取りですが、onEach にリソース書き込み文を含めると項目ごとにその書き込みが実行され、状態が変わります。
4 つの文(ResourceRead・ResourceFind・ResourceForEach・ResourceCount)はいずれも from(デフォルト Current)で、どの保存版を読み取るかを指定します。Current はコンテンツスタジオに表示される最新の下書き(CMA/ACMA が読む値)で、Published は公開スナップショット(CDA/ACDA が配信する、最後に公開した時点の値)です(ServiceUser は公開されないため Current のみを受け取ります。会員ディレクトリの読み取り 参照)。
ResourceFind・ResourceForEach・ResourceCount はこれに加えて advanced(デフォルト true)で 高度な検索(Advanced Search) の有効と無効を切り替えます。書かなければ有効です。 Content 専用のため、Media・ServiceUser の読み取りでは無視されます。有効にすると where の regex・near・within 演算子とテキスト全文検索(全文検索が有効な LongText フィールドでは、eq が値を含む項目まで部分・類似マッチで見つけます)、そして fields.* での並べ替えを使えます。無効にするとこの 3 つの演算子は拒否され、テキストの eq は完全一致になり、prefix と比較・リスト演算子は高度な検索に関係なく動作します。作成または変更したばかりの項目は高度な検索への反映に少し(約 1 秒)かかるため、その直後の高度な検索では拾えないことがあります。デフォルトが有効なので、この遅延は advanced を false にしない限りすべての照会に当てはまります。書き込んだばかりの項目をすぐに読み取る必要がある場合は、id で ResourceRead(基本の保存版で、反映の遅延はありません)を使うか、書き込みが返した sys.id で取得します。
where の createdBy: ":self" は「今呼び出したユーザーが作成したものだけ」を意味します。ただし匿名呼び出しを許可した Script(anonymousCallEnabled)では使えません。その場合 :self が呼び出し元ではなく作成者に解決され、作成者のリソースが黙って開いてしまうため、そうした定義は保存が拒否されます(匿名呼び出し 参照)。
where と order では、コンテンツのフィールドは fields.<field> と書きます(名前だけでは認識されません)。fields.<field> には Space の既定ロケールが自動的に適用されるため、ロケールを直接付けません。下の例の fields.status・fields.slug がそのまま既定ロケールの照会です。特定(非既定)のロケールだけを照会する場合にのみ、fields.<field>.<locale>(例: fields.title.ko-KR)で明示します。sys.*(sys.createdAt など)と createdBy(:self)は fields. を付けずにそのまま書きます。詳しい規則は 値式の where と order のロケール にあります。
会員ディレクトリの読み取り (ServiceUser)
ResourceRead・ResourceFind・ResourceForEach は resource に "ServiceUser" を受け取り、その Space の会員ディレクトリを読み取ります(ResourceCount は受け取りません。下の ResourceCount 参照)。注文の持ち主が誰かを確認したり、メールアドレスで会員を探してその sys.id を次の文に渡す流れで使います。以下の規則は 3 文に共通です。
- 読み取りだけができます。
ResourceCreate・ResourceUpdate・ResourcePatch・ResourceDeleteと公開・アーカイブの文は"ServiceUser"を受け取らず、そうした定義は保存の時点で拒否されます。権限を足して開けるものではなく、Script から会員を変更する道がそもそもないため、権限エラーではなく書き方を誤った文として拒否されます。 - 作成者に会員ディレクトリの権限がなければ保存できません。 Content・Media のように権限マップで検査するのではなく、作成者の SpaceRole
settingsにSETTING_SERVICE_LOGIN(またはSETTING_ALL)があるかを見ます。会員ディレクトリは他のすべての経路でも Space 設定が管掌するリソースだからです。なければ保存が拒否されます(セキュリティモデル 参照)。 fromはCurrentのみを受け取ります。会員は公開されるリソースではないため、Publishedを渡すと実行が失敗します。contentTypeとadvancedは無視されます。 会員ディレクトリは Content Type で分かれず(Space 全体で 1 組)、高度な検索も Content 専用です。whereのsys.emailは完全一致系の演算子のみを受け取ります(eq・ne・in・nin)。会員のアドレスは暗号化して保存されるため、順序比較やprefixには意味がありません。それ以外の演算子を渡すと、黙って 0 件を返すのではなく実行が失敗します。- 結果は ServiceUser リソースそのものです。
{ /<name>/sys/id }、{ /<name>/nickname }のように参照します。構造は ServiceUser リファレンス で扱います。見つけた会員にメールを送るときはアドレスを取り出さず、EmailSendのtoServiceUserにそのsys.idを渡します(エンジンが送信の直前にアドレスを resolve するため、会員のアドレスが Script の変数空間に入りません)。
// メールアドレスで会員 1 名を探す。なければ null
{ "type": "ResourceFind", "resource": "ServiceUser",
"where": { "sys.email": { "eq": "{ /payload/fields/email }" } }, "name": "member" }ResourceRead
id で単一件取得します(get-by-id)。結果はリソース全体を名前にバインドします。
| フィールド | 説明 |
|---|---|
resource | "Content"・"Media"・"ServiceUser" |
target | 対象({ sys: { id } })。id は値式 |
from | (任意)Current(デフォルト、最新の下書き)または Published(公開スナップショット)。ServiceUser は Current のみ |
- 結果: リソースそのものがバインドされます。この文に
nameを付けていれば{ /<name>/sys/id }、{ /<name>/fields/<field>/<locale> }で直接参照します(下の例の"name": "order"なら{ /order/sys/id })。一覧ではないので配列インデックスを経由しません。 - 対象がなければエラーです。
Tryで囲んで処理できます。
{ "type": "ResourceRead", "resource": "Content",
"target": { "sys": { "id": "{ /payload/fields/orderId }" } }, "name": "order" }ResourceFind
フィルターで最初にマッチした単一件を読み取ります。なければ null です。ユニークな業務キー(slug、email、sku)で 1 件を探すときに使います。
| フィールド | 説明 |
|---|---|
resource | "Content"・"Media"・"ServiceUser" |
contentType | 検索範囲の Content Type({ sys: { id } })。Content のとき必須。Media・ServiceUser では無視 |
where | フィルター({ "<field>": { "<op>": <値> } })。使える演算子は 演算子一覧 に従います(regex・near・within は advanced が必要)。createdBy: ":self" をサポート。ServiceUser の sys.email は eq・ne・in・nin のみ(会員ディレクトリの読み取り) |
order | 複数マッチ時に「最初」を決定するソート(例: "-sys.createdAt") |
from | (任意)Current(デフォルト、最新の下書き)または Published(公開スナップショット)。ServiceUser は Current のみ |
advanced | (任意)高度な検索(Advanced Search)で実行。Content 専用(Media・ServiceUser は無視)。デフォルト true。上の リソース読み取り の説明を参照 |
- 結果: 最初にマッチしたリソースを
nameにバインドします。{ /<name>/fields/<field>/<locale> }で直接参照します。なければnullなので、{ "==": [ "{ /<name> }", null ] }で存在の有無を分岐します(find-then-upsert の典型)。
{ "type": "ResourceFind", "resource": "Content", "contentType": { "sys": { "id": "ct_article" } },
"where": { "fields.slug": { "eq": "{ /payload/fields/slug }" } }, "name": "found" }ResourceForEach
フィルターにマッチするリソースを内部的に巡回し、項目ごとに onEach を実行します。値として使うコレクションを作るのではなく、各項目に対して作業を実行するための文です。draft の一括公開、条件にマッチする Content の一括修正、各項目を外部へ送信・同期するといった反復作業に使います。1 件だけ読むときは ResourceRead(id)や ResourceFind(フィルター)を使います。
| フィールド | 説明 |
|---|---|
resource | "Content"・"Media"・"ServiceUser"(必須) |
contentType | 巡回範囲の Content Type({ sys: { id } })。Content のとき必須。Media・ServiceUser では無視 |
where | フィルター({ "<field>": { "<op>": <値> } })。意味は ResourceFind の where と同じです(ServiceUser の sys.email の制約も同じです)。使える演算子は 演算子一覧 に従います(regex・near・within は advanced が必要)。createdBy: ":self" をサポート |
order | ソート(例: "sys.createdAt,sys.id")。なければプラットフォームの既定順 |
from | Current(デフォルト、最新の下書き)または Published(公開スナップショット)。ServiceUser は Current のみ |
advanced | 高度な検索(Advanced Search)で巡回。Content 専用(Media・ServiceUser は無視)。デフォルト true。上の リソース読み取り の説明を参照 |
limit | (任意、1 以上) 総処理件数の上限(ページサイズではありません)。なければプラットフォームの上限(10,000 件)まで巡回 |
name | (任意) 現在の項目をバインドする名前。繰り返しごとに新しくバインドされ、onEach 内で { /<name> } で参照します(Loop の name と同じ寿命。巡回が終わった後も最後の項目がバインドされたまま残ります)。項目を参照しない場合は省略します |
onEach | 各項目ごとに実行する子 statement 配列(必須) |
- コレクションをバインドしません(
mapではなくforeach)。{ items, next }も cursor もありません。巡回の結果を値として受け取るのではなく、項目ごとにonEachを回します。一覧が必要ならSetVarで自分で集めます。件数だけが必要ならResourceCountを使います。 limitがなくても無限巡回ではありません。 なければプラットフォームの上限(10,000 件)まで回り、マッチが残ったままその上限に達すると失敗します(手をつけていない項目を残したまま成功と報告しないためです)。逆に 宣言したlimitに到達するのは意図した停止なので正常終了です。上限を超えるlimitは保存時に拒否されます。- cursor がありません。 完走すれば成功で、途中で止まれば(実時間・クォータ超過、
onEachの未処理の失敗)失敗であり、どの項目でなぜ失敗したかをエラーが指し示します。再開は作成者が自分のデータで表現します(whereを「未処理」にしてonEachの最後で完了を記録すれば、再実行で残ったものから続きます)。 - 時間予算では掛け算として計上されます。 この文が宣言する時間は、
onEachが宣言した時間に処理する項目数(limit、なければ 10,000)を掛けた値です(時間予算)。子を所有する複合 statement なので、それ自体は 外部呼び出しの leaf 予算 には数えず、onEach内の外部呼び出し文が予算に数えられます。 onEachには他の statement と同様に外部呼び出し(Http・EmailSend)や Media ファイルのインジェストを含められます(Loopのbodyと同じです)。リソースのクエリ結果を項目ごとに一度ずつ処理することが、この文の存在理由です。
// draft 状態の投稿をすべて見つけて項目ごとに公開
{ "type": "ResourceForEach", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
"where": { "fields.status": { "eq": "draft" } }, "order": "sys.createdAt,sys.id",
"from": "Current", "advanced": false, "name": "post",
"onEach": [
{ "type": "ResourcePublish", "resource": "Content", "target": { "sys": { "id": "{ /post/sys/id }" } } }
] }ResourceCount
フィルターにマッチする件数だけを数えます。項目を読み取ってこないため、一覧ではなく個数が必要なときに使います。残っている在庫を確認したり、同じ値がすでにあるかを判定したり、限度を超えたかを検査する場面です。
| フィールド | 説明 |
|---|---|
resource | "Content" または "ContentType"(必須)。Media・ServiceUser は数えられず、そう書くと保存が拒否されます |
contentType | 数える範囲の Content Type({ sys: { id } })。Content のとき必須。Content Type を数えるときは無視されます(Space 全体で 1 組) |
where | フィルター。意味は ResourceFind の where と同じです。マッチした項目をすべて数えます |
from | (任意)Current(デフォルト、最新の下書き)または Published(公開スナップショット) |
advanced | (任意)高度な検索(Advanced Search)で実行。Content 専用(Content Type を数えるときは無視)。デフォルト true。上の リソース読み取り の説明を参照 |
name | (任意)件数をバインドする名前 |
- 結果: マッチした件数を
nameにバインドします。{ /<name> }で参照して、比較や分岐に使います。 - 項目は返しません。 項目が必要なら
ResourceFind(最初にマッチした単一件)やResourceForEach(項目ごとに実行)を使います。 - 件数を得るために
ResourceForEachで巡回して数えないでください。 巡回は時間予算を項目数の分だけ掛けて確保し(時間予算)、マッチが残ったままプラットフォームの上限に達すると失敗します。数えるだけでよいなら、この文が一度で終わらせます。 orderとlimitはありません。数えるのに順序は必要なく、マッチしたものはすべて数えるからです。
// この投稿に付いたコメントが何件あるかを数える
{ "type": "ResourceCount", "resource": "Content", "contentType": { "sys": { "id": "ct_comment" } },
"where": { "fields.postId": { "eq": "{ /payload/sys/id }" } }, "name": "commentCount" }外部
Http
外部 HTTP を呼び出します。外部呼び出しなので、プランごとの 外部呼び出しの上限 に数えられ、時間予算 には timeoutMs(なければ 30 秒)× (1 + retry)として計上されます。
| フィールド | 説明 |
|---|---|
method | "GET", "POST", "PUT", "PATCH", "DELETE" |
url | 対象 URL(値式。{ /ptr } を挿入可能) |
headers | [{ "key", "value", "secret"? }]。value は値式。secret:true のヘッダーは CMA(管理者)専用として扱われ、エンドユーザーには公開されず、送信直前にのみ復号されます。Content-Type をここに入れると、body がその形式でシリアライズされます(下記) |
body | リクエスト body(値式または JSON)。どの形式で送られるかは Content-Type ヘッダーが決めます |
timeoutMs | この呼び出しのタイムアウト(ms) |
retry | 応答 status が 400 以上の場合に再試行する回数。デフォルト 0、上限は 2 |
ignoreStatusCode | (再試行を終えた)最終 status が 400 以上のとき、この呼び出しを失敗とみなすか。false(デフォルト)の場合は失敗として扱われ、Try/catch の対象になります。true の場合は失敗とみなさず、{ status, body } をそのままバインドします(呼び出し元が status で直接分岐) |
responseType | レスポンス本文を何として受け取るか。"Json"(デフォルト)はオブジェクト・配列にパースし、"Text" は文字列として受け取ります |
- 結果:
{ status, body }です。この文にnameを付けていれば{ /<name>/status }、{ /<name>/body/... }。bodyの形はresponseTypeが決めます。 responseTypeは成功レスポンスにのみ適用されます。 status が 400 以上のレスポンスの本文は、宣言した値に関わらず診断用としてバインドされます(JSON ならパースされた値、そうでなければ文字列)。"Json"なのに本文が JSON でない場合、この呼び出しは失敗します(Try/catchの対象)。JSON を返さない API は"Text"で受け取り、値として扱う必要があればParseJsonでパースします。"Text"はレスポンスのContent-Typeの charset でデコードし、charset がなければ UTF-8 とみなします。本文が空の場合、bodyはどちらもnullです。- 応答サイズの上限: 応答本文は最大 10MiB です。これを超えると、この呼び出しは例外で失敗し、他のランタイム失敗と同様に
Try/catchで処理できます(サイズによる失敗なのでignoreStatusCodeでは無視されません)。
{ "type": "Http", "method": "POST", "url": "https://api.llm.com/v1/gen",
"headers": [ { "key": "Authorization", "value": "Bearer sk-...", "secret": true } ],
"body": { "prompt": "{ /payload/fields/prompt }" }, "timeoutMs": 15000, "retry": 1,
"responseType": "Json", "name": "resp" }body がどの形式で送られるか
headers に入れた Content-Type が body のシリアライズ形式を決めます。比較では大文字と小文字の違いや ;charset=… のようなパラメーターを無視し、前の部分だけを見ます。ヘッダーがない場合や値が空の場合は application/json で送ります。このヘッダーは body があるときにだけ付くので、body がなければ書いておいたヘッダーがそのまま送られます。同じキーを複数回入れると、最初の値だけが使われて 1 つにまとめられます。
宣言した形式に収められない body は、収められる形式に訂正して送ります。 ヘッダーの宣言と実際の body が食い違うことはありません。
宣言した値のまま送られる組み合わせです。
宣言した Content-Type | body の形 | 送られる body |
|---|---|---|
application/json | 何であれ | JSON |
application/x-www-form-urlencoded | オブジェクト・配列 | order[id]=A-2481&order[amount]=34000 |
text/plain | スカラー | 値そのまま |
その他(text/xml など) | 何であれ | JSON |
宣言した形式には収められないため訂正される組み合わせです。
宣言した Content-Type | body の形 | 実際に送られる Content-Type | 送られる body |
|---|---|---|---|
application/x-www-form-urlencoded | スカラー | text/plain;charset=UTF-8 | 値そのまま |
text/plain | オブジェクト・配列 | application/json | JSON |
この 2 行は、噛み合わない組み合わせのときにリクエストがどう送られるかを明らかにしたものであり、意図した形式を得る方法ではありません。body を値式で組み立てると、実行時点の payload によってスカラーになることがあり、そのときこの訂正はエラーなしに行われます。受け取る側が形式を問題にする場合は、body の形と Content-Type のどちらかを意図に合わせて修正してください。
form-urlencoded はオブジェクトをブラケットキーに、配列をインデックスに展開します。
body | 展開されるキーと値 |
|---|---|
{ "order": { "id": "A-2481", "amount": 34000 } } | order[id]=A-2481&order[amount]=34000 |
{ "tags": ["outerwear", "winter"] } | tags[0]=outerwear&tags[1]=winter |
{ "items": [{ "sku": "TUMBLER-500" }] } | items[0][sku]=TUMBLER-500 |
{ "memo": null } | memo= |
キーと値は UTF-8 でパーセントエンコードされて送られます。上の表は、キーの構造を示すためにデコードした形です。値に & や + が含まれていても、ペアの区切り文字や空白と誤解されることなくそのまま伝わります。
ネストをブラケットキーに展開する表記は広く使われている慣例であり、形式そのものの規格ではありません。 受け取る側が order[id] をネストしたオブジェクトに復元するかを確認し、復元しない場合はフラットなキーで body を構成してください。
{ "type": "Http", "method": "POST", "url": "https://api.example.com/oauth/token",
"headers": [ { "key": "Content-Type", "value": "application/x-www-form-urlencoded" } ],
"body": { "grant_type": "client_credentials", "client_id": "{ /vars/clientId }" },
"name": "token" }EmailSend
登録済みの EmailAccount を通じて メール 1 通を送ります。受け取るフィールドは SMTP/MIME にそのままマッピングされるものだけです。テンプレート id、予約送信、プロバイダー固有の拡張はありません(そうした機能が必要なら Http で該当メールサービスの API を直接呼び出します)。送信者(送信元アドレス)はここで定めず、account が指す EmailAccount から来ます。
| フィールド | 説明 |
|---|---|
account | 送信に使う EmailAccount 参照({ sys: { id } }、必須)。通常はリテラル id です。値式で渡すと送信時点で resolve されるため、保存時には検査できません |
to | 受信者アドレス(値式)。to と toServiceUser のうち ちょうど 1 つだけを使います |
toServiceUser | 受信者を ServiceUser 参照で指定({ sys: { id } }。その sys.id は値式が可能)。エンジンが 送信の直前にアドレスを resolve するため、メンバーのアドレスが Script の変数空間に入りません |
cc | Cc 受信アドレスの配列(値式) |
bcc | Bcc 受信アドレスの配列(値式) |
subject | 件名(値式、必須) |
body | 本文(値式、必須)。常に text/html で送信されるため、プレーンテキストではなくマークアップを書きます(改行は空白に、< はタグとして解釈されます)。補間される値式の結果は HTML エスケープ されます |
replyTo | (任意)Reply-To ヘッダー(値式)。送信者と異なることがあります(例: no-reply で送りつつ返信はサポートアドレスへ) |
timeoutMs | (任意、1 以上)この送信のタイムアウト(ms)。なければプラットフォームのデフォルト値、上限を超える値は保存時に拒否 |
- 受信者の合計は最大 50 名です。
to(1 名)、cc、bccをすべて合わせて数えます(SMTP のエンベロープには cc/bcc の区別がなく、すべて受信者として送られるため合計で数えます)。超えると保存・実行で拒否されます。多くの人に送るにはResourceForEach+EmailSendで 項目ごとに 1 通ずつ送ります。 - 結果をバインドしません。 成功は「プロバイダーがメールを受け付けた」ことだけなので返す値がなく、
nameを受け取りません。再試行もしません(メールは非冪等であり、曖昧に失敗した後に再試行すると重複送信になります。そのためHttpのretryに従いません)。失敗は throw され、Tryのcatchで処理します。 - 外部呼び出しです。 プランごとの 外部呼び出しの上限 にカウントされ、時間予算 には
timeoutMs(なければ 10 秒)1 回分として計上されます(再試行しないため、Httpのように回数を掛けません)。ResourceForEachのonEach内で使えます(多件送信の標準形)。
{ "type": "EmailSend", "account": { "sys": { "id": "eml_orders" } },
"to": "{ /order/fields/email/en-US }",
"subject": "注文受付完了 (注文番号 { /order/sys/id })",
"body": "<p>ご注文を受け付けました。配送が始まりましたら再度お知らせします。</p>",
"replyTo": "support@my-shop.example" }変数
SetVar
script-scoped な可変変数を宣言または更新します。{ /vars/<var> } で参照します(JsonLogic には変数宣言がないため statement として提供します)。
| フィールド | 説明 |
|---|---|
var | 変数名。{ /vars/<var> } で参照 |
value | 値式。自分自身を参照して累積できます |
{ "type": "SetVar", "var": "total", "value": 0 }
{ "type": "SetVar", "var": "total", "value": { "$+": [ "{ /vars/total }", "{ /row/qty }" ] } } // 累積
{ "type": "SetVar", "var": "ids", "value": { "$merge": [ "{ /vars/ids }", [ "{ /row/sys/id }" ] ] } } // 配列の収集キャッシュ
Cache
その Script だけの短命なキャッシュを読み書きします。外部呼び出しの結果のように、毎回取り直すのが惜しい値を数秒のあいだ持っておき、次の呼び出しで再利用するための箇所です。外部呼び出しではないので、1 定義あたりの外部呼び出し数には含まれず、時間予算に宣言する時間もありません。
| フィールド | 説明 |
|---|---|
action | "Set"(書き込み)、"Get"(読み取り)、"Delete"(削除)のいずれか(必須) |
key | キャッシュキー(Cache Key)(必須)。値式ではなくリテラルです(下記参照)。最大 128 文字で、超えると保存が拒否されます |
value | 保存する値(Set 専用)です |
ttl | キャッシュが生きている時間(Set 専用、秒)。1 から 30 のあいだで、省略すると 5 です |
defaultValue | キャッシュされたデータがないときに Get がバインドする値(Get 専用)。省略すると null です |
name | 結果を入れる名前。Get は必須です(読み取った値の行き先がなければ、読み取る理由がありません)。Set は保存した値を、Delete は削除を実行したかどうかをバインドし、どちらも任意です |
- その演算に該当するフィールドだけを書きます。
Getにttlを書いたり、SetにdefaultValueを書いたりすると保存が拒否されます。 - ないことと期限切れは区別されません。 どちらも
defaultValueがバインドされます。nullを保存しておいた場合も同じです。 - 保存の範囲はその Script 1 つです。 同じ Space の他の Script は、同じキャッシュキーを使っても互いのデータを見られません。その Script を修正または削除すると、その Script のデータはすべて消えます。
keyはリテラルです。 リクエストから来たキャッシュキーでデータを選ばせると、呼び出し元が何を読むかを決めることになり、会員ごとに 1 つずつ入れておいた Script が、ある会員の値を別の会員に渡してしまいます。そのためkeyの中に{ /pointer }があると、値に置き換えられることも文字どおり使われることもありません。保存そのものが拒否されます。- 繰り返しの中には置けません。
LoopやResourceForEachのブロックの中にCacheがあると保存が拒否されます。下記の個数の上限が、繰り返しの中では何の制限にもならないからです。繰り返しごとに 1 つずつデータを書くことになるため、定義に書かれた文の数と実際に使われるキャッシュキーの数が食い違います。 - 1 つの定義に 5 個まで入れられます(ネスト含む、演算に関係なく合算)。超えると保存が拒否されます。
- 保存する値は 10,240 バイト(10KiB)までです。 超えるとその文が失敗します(status 422)。他のランタイム失敗と同じなので、
Try/catchで局所的に処理できます。
// 為替レートを 30 秒のあいだ再利用します。
{ "type": "Cache", "action": "Get", "name": "cached", "key": "rates" }
// 持っている値があれば、外部呼び出しなしでそのまま返します
{ "type": "If", "condition": { "!!": [ "{ /cached }" ] },
"then": [ { "type": "Return", "value": "{ /cached }" } ] }
{ "type": "Http", "name": "fetched", "method": "GET", "url": "https://api.example.com/rates" }
{ "type": "Cache", "action": "Set", "key": "rates", "value": "{ /fetched/body }", "ttl": 30 }
{ "type": "Return", "value": "{ /fetched/body }" }
// 持っていた値を期限切れの前に捨てます
{ "type": "Cache", "action": "Delete", "key": "rates" }値のパース
ParseJson
JSON テキストを、それが表す値にパースして名前にバインドします。Http を responseType: "Text" で受け取った本文、payload で渡された JSON 文字列、フィールドに文字列として保存しておいた JSON を扱うときに使います。外部呼び出しではないので外部呼び出しの上限には数えられず、時間予算に宣言する時間もありません。
| フィールド | 説明 |
|---|---|
name | パース結果を入れる名前(必須)。他の文では任意ですが、ここでは必須です。結果をバインドする以外に何もしないため、名前がないと何の効果もない文になります |
value | パースする JSON テキスト(値式、必須)。{ /resp/body } のように前の段階の値を指すか、JSON テキストをリテラルでそのまま書きます(リテラル内の { は { ポインター } テンプレートとして解釈されません) |
- 結果: パースされた値そのものです。オブジェクトはオブジェクト、配列は配列、
42や"a"のような単一の値もそのままパースされます。以降は{ /<name>/... }で内部を指します。 - すでにパースされた値が来た場合はそのままバインドします。
valueが文字列でない値に resolve された場合、パースするテキストではないと判断してその値をそのまま入れます。 - パースしたテキスト内の
{ /pointer }は再度解釈しません。 外部から受け取った文字列が{ /payload/... }のような表現を含んでいても、値に置き換えられず文字列として残ります。 nullは 2 つの場合を区別します。 パースするテキストがnullの一語であれば正常で、結果もnullです。一方valueが指す場所が空で値そのものがない場合は、パースするものがないため失敗です。- 失敗:
valueが値なしに resolve された場合や空白だけの場合、そしてテキストが JSON でない場合です。他のランタイム失敗と同じくTry/catchで処理し、エラーメッセージにパースしようとしたテキストが載ります。 - 定義あたりの statement 数には 1 つとして数えますが、外部呼び出しの上限や
SetVarの上限とは無関係です。
// 1) JSON を返さない API: Text で受け取ってパース
{ "type": "Http", "method": "GET", "url": "https://api.partner.example/v1/quote",
"responseType": "Text", "name": "resp" },
{ "type": "ParseJson", "name": "quote", "value": "{ /resp/body }" },
// 2) payload で渡された JSON 文字列をパース
{ "type": "ParseJson", "name": "spec", "value": "{ /payload/fields/specJson }" }署名の検証とテキスト処理
決済代行会社が Webhook で送ってきた署名を確認し、その署名が包まれて届いた文字列を解きほどく文です。3 つとも外部呼び出しではなく計算なので、外部呼び出しの上限には数えられず、時間予算に宣言する時間もありません。また、データの箇所を持たないため $ プレフィックスの規則 とは無関係です。3 つを組み合わせた完結した例は クックブックの Webhook 署名検証 にあります。
3 つの文はいずれも resolve された値の長さに上限があります。式の長さではなく、その式が指した値の長さであり({ /rawPayload } の 16 文字が数十 KB を指します)、超えると実行が失敗するので Try で処理できます。数値は 値の長さの上限 にまとめてあります。
Signature
受け取った署名コードが secret で作ったコードと同じかを確認し、その答えを Boolean 値としてバインドします。決済代行会社(PG・MoR)が Webhook で送ってくる署名は、この文で検証します。
| フィールド | 説明 |
|---|---|
name | 検証結果を入れる名前(必須)。{ /<name> } が true または false です。検証しておいて結果を使わないのは検証していないのと同じなので、省略できません |
algorithm | コードを作るハッシュ(必須)。SHA1・SHA256・SHA384・SHA512 |
secret | 相手と共有した秘密鍵(値式、必須) |
secretEncoding | secret をどの表記で書いたか。Utf8(デフォルト、テキストの鍵)・Hex・Base64。hex や base64 で発行された鍵をテキストのまま置くと別の鍵になり、それらしいコードは作られるものの永遠に一致しません |
value | コードを計算するメッセージ(値式、必須)。相手が署名したバイトと文字どおり同じでなければならないため、通常は { /rawPayload } か、プロバイダーがヘッダーに一緒に載せて送ってきたタイムスタンプをその前に付けたものです |
expected | 呼び出し元が送ってきたコード(値式、必須)。例: { /headers/x-signature } |
- 結果:
Booleanです。以降はIfの条件に{ /<name> }をそのまま書きます。 valueはパースされた/payloadではなく/rawPayloadで書きます。 パースした payload を再び文字列に戻すと、空白・数値表記・エスケープが正規化され、相手が署名したバイトには戻りません(コンテキストルート)。- 出力の表記を指定するフィールドはありません。
algorithmがコードのバイト長を固定し、同じ長さの hex と base64 は文字列の長さが重ならないため、相手がどちらで送ってきたかを教えてもらわなくてもエンジンがバイトを復元します。hex の大文字・小文字、base64 と base64url(パディングの有無を含む)も同じ理由で区別しません。 - 失敗と
falseは、その値を誰が与えるかで分かれます。expectedがない場合やコードが一致しない場合は、結果がfalseになるだけで失敗ではありません。ヘッダーの欠落とコードの不一致を別々に知らせると、どちらが違っているのかを送信元に教えることになるからです。valueが空なら空のメッセージで計算します。空の本文も署名の対象です。secretがない場合やsecretEncodingが宣言した表記になっていない場合は失敗です。3 つのうち作成者自身の入力はこれだけです。失敗メッセージにsecretとvalueは載りません。
valueの上限は 65,536 文字です(resolve された値が基準)。実際のプロバイダーが送ってくる Webhook 本文の大きさに合わせた値です。- 比較は値が同じかどうかを constant-time で判定します。先頭の何バイトが一致したかが応答時間から漏れません。
secretは暗号化して保存されません。Httpヘッダーのsecret: true(暗号化して保存し、送信の直前に復号)と違って定義に書いたまま残るため、その Script を読める役割には値が見えます。会員(ServiceUser)は Script の定義を読めません(オーサリングと照会は CMA 専用)。
// 本文全体に署名するプロバイダー
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
"secret": "whsec_9f2c1b7ae4", "value": "{ /rawPayload }",
"expected": "{ /headers/x-webhook-signature }" }
// 鍵を base64 で発行するプロバイダー
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
"secret": "aGVsbG8td2VlZ2xvbw==", "secretEncoding": "Base64",
"value": "{ /rawPayload }", "expected": "{ /headers/webhook-signature }" }Hash
value をダイジェストして、encoding が定めた表記の文字列としてバインドします。HMAC ではなく「いくつかのフィールドと秘密鍵をつなげて SHA256 を計算する」署名スキームを再現するときに使います。
| フィールド | 説明 |
|---|---|
name | ダイジェストを入れる名前(必須) |
algorithm | MD5・SHA1・SHA256・SHA384・SHA512(必須)。MD5 はそれを要求する以前のスキームを再現するためのもので、新しく作る署名で選ぶ値ではありません |
value | ダイジェストするメッセージ(値式、必須) |
encoding | 結果の表記。Hex(デフォルト)・HexUpper・Base64・Base64Url |
secretフィールドはありません。 スキームごとに鍵が前・後・中間と分かれるため、鍵をvalueの中に直接書くほうがすべての位置を表現できます。- 結果: 文字列です。相手が送ってきたコードと比較するときは
{ "==": [ "{ /<name> }", "{ /headers/... }" ] }と書きます。この比較はSignatureの constant-time 比較とは違い、通常の等価比較です。 valueが値なしに resolve された場合や空白だけの場合は失敗です(作成者自身の値式だからです)。valueの上限は 128 文字です。つなげたフィールドをいくつか入れる箇所なのでSignatureよりはるかに狭いです。Webhook の本文全体を対象に計算する必要がある場合はSignatureを使います。
// SHA256(注文番号 + 金額 + merchantKey) を大文字 hex で
{ "type": "Hash", "name": "expectedSign", "algorithm": "SHA256", "encoding": "HexUpper",
"value": "{ /payload/orderId }{ /payload/amount }9f2c1b7ae4" }Regex
pattern を value に適用して、mode が求めたものをバインドします。値式には文字列を切り出す手段がないため(つなげる cat と、含むかを見る in だけです)、t=…,v1=… のように 1 つのヘッダーに複数の値が包まれて届くものを解くときにこの文を使います。
| フィールド | 説明 |
|---|---|
name | 結果を入れる名前(必須)。Capture は要素を { /<name>/1 } で指します |
mode | "Match" は一致の有無を Boolean で、"Capture" は最初のマッチを配列でバインド(必須) |
pattern | 正規表現(必須)。値式ではなくリテラルです(下記参照)。フラグは (?i) のようにパターンの中に書きます。最大 128 文字で、超えると保存が拒否されます |
value | パターンを適用するテキスト(値式、必須)。resolve された値が 10,240 文字(10KiB)を超えると実行が失敗します |
- 結果:
MatchはBoolean、Captureは配列またはnullです。配列はインデックス0がマッチ全体で、1からがキャプチャグループであり、参加しなかったグループはnullです(空文字列ではありません。それはマッチしたということです)。パターンが現れなければCaptureは空の配列ではなくnullです。 - どちらのモードも「パターンがどこかに現れるか」を問います。 テキスト全体がパターンと同じでなければならない場合は
^…$で固定します。Matchで検査してからCaptureで取り出す 2 つの文が互いに違う答えを出さないよう、問いを同じにしてあります。 patternはこのエンジンで値式ではない 2 つのフィールドのうちの 1 つです(もう 1 つはCacheのkeyです)。リクエストから来たパターンをそのまま実行すると、呼び出し元が実行される式を選べることになり、正規表現のバックトラッキングがそれをサービス拒否の手段にします。そのため、パターンの中の{ /pointer }も値に置き換えられず、文字どおりパターンの一部になります。- パターンは実行が始まるときに定義全体で 1 回コンパイルされます。
LoopやResourceForEachの中にあっても繰り返しごとに再コンパイルされることはなく、使えないパターンは最初の文が何かをする前に失敗します(Tryで処理可能)。
// "t=1492774577,v1=<64 文字の hex>" を解いて { /sig/1 } = タイムスタンプ、{ /sig/2 } = コード
{ "type": "Regex", "name": "sig", "mode": "Capture",
"pattern": "^t=(\\d+),v1=([0-9a-f]{64})$", "value": "{ /headers/x-provider-signature }" }
// 形式だけを検査
{ "type": "Regex", "name": "isOrderId", "mode": "Match",
"pattern": "^ORD-\\d{8}-\\d{4}$", "value": "{ /payload/orderId }" }制御フロー
If
条件分岐です。condition は JsonLogic で、真偽は 真偽判定 の規則に従います。
| フィールド | 説明 |
|---|---|
condition | JsonLogic(boolean として評価) |
then | 真の場合に実行する Statement 配列 |
else | (任意)偽の場合に実行する Statement 配列 |
{ "type": "If",
"condition": { "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] },
"then": [ { "type": "Return", "value": { "ok": false, "reason": "insufficient credit" } } ],
"else": [ /* ... */ ] }Loop
繰り返しです。モードを 1 つ選びます。over(foreach)、while(条件)、for(カウント)です。どのモードでもエンジンが繰り返しの上限を強制します(無限ループの防止)。上限は maxIterations で宣言し、書かなければプラットフォームの上限が適用されます。body 内には外部呼び出し(Http・EmailSend)や Media ファイルのインジェストも含められ、外部呼び出し文は実行時に繰り返しごとに実際に呼び出されます。1 つの定義あたりの外部呼び出しの最大数の制限は、そのまま適用されます。
時間予算では掛け算として計上されます。 この文が宣言する時間は、body が宣言した時間に maxIterations(なければ 10,000)を掛けた値です(時間予算)。body に外部呼び出しがなければ宣言時間は 0 なので、30 秒の基本予算が実質的な上限です。
| フィールド | 説明 |
|---|---|
over | foreach: 配列に resolve される値式 |
while | 条件: JsonLogic(真である間繰り返す) |
for | カウント: { "from", "to", "step"? }。from から to まで含む、step はデフォルト 1 |
maxIterations | 最大繰り返し回数(任意)。書かなければプラットフォームの上限 10,000 が適用され、それより大きい値は保存時に拒否されます |
name | (任意)現在の項目(foreach)やインデックス(while・for)をバインドする名前({ /<name> }) |
body | 繰り返し本体の Statement 配列 |
// foreach
{ "type": "Loop", "over": "{ /payload/fields/items }", "name": "item", "maxIterations": 100,
"body": [ { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_item" } },
"fields": { "name": { "en-US": "{ /item/name }" } } } ] }
// while
{ "type": "Loop", "while": "{ /vars/hasMore }", "maxIterations": 1000, "body": [ /* ... */ ] }
// counted (1..10 step 2)
{ "type": "Loop", "for": { "from": 1, "to": 10, "step": 2 }, "name": "i", "maxIterations": 100, "body": [ /* ... */ ] }Parallel
ブランチを同時に実行し、join してから進みます。ブランチ間の参照は不可です(依存がある場合は順次に配置します)。
| フィールド | 説明 |
|---|---|
branches | Statement[][]。各要素が 1 つのブランチ(文の配列) |
{ "type": "Parallel", "branches": [
[ { "type": "Http", "method": "GET", "url": "https://api.a.com/x", "name": "a" } ],
[ { "type": "Http", "method": "GET", "url": "https://api.b.com/y", "name": "b" } ]
] }Return
一般的なプログラミングの return です。Script の結果を呼び出し元に返し、その地点で正常終了します。
| フィールド | 説明 |
|---|---|
value | (任意)返す値式 |
isError | デフォルト false。true の場合は value が応答の error として返ります(そうでなければ return) |
statusCode | 応答ステータスコード。デフォルト 200 |
Returnに到達しなければ返り値はありません。結果を返すにはvalueを明示します。- 例外や throw ではなく正常終了なので
catchの対象ではありません(Tryの中でも Script 全体を終了しますがfinallyは実行します)。 - guard もこの文で表現します。
IfのthenにReturnを置くと、条件に反したときに値を返し、以降の文を実行しません。これはReturnの複数ある使い方のうちの 1 つです。
{ "type": "Return", "value": { "orderId": "{ /order/sys/id }", "status": "paid" }, "statusCode": 201 }
{ "type": "Return", "value": { "reason": "payment failed" }, "isError": true, "statusCode": 402 }Try
例外処理です。
| フィールド | 説明 |
|---|---|
body | 試行する Statement 配列 |
catch | (任意)body 失敗時に実行。/error に { message } を露出(どの文で失敗したかは入りません) |
finally | (任意)成功・失敗に関わらず常に実行 |
catchが処理すれば Script は中断されません。catchのない失敗のみが Script を中断させます(補償の試行を含む)。- 何が「失敗」なのか、補償(compensation)の限界は 実行セマンティクス、制約、セキュリティ で扱います。
{ "type": "Try",
"body": [ { "type": "Http", "method": "POST", "url": "https://primary.api/gen", "name": "resp" },
{ "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_result" } },
"fields": { "text": { "en-US": "{ /resp/body/text }" } } } ],
"catch": [ { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_result" } },
"fields": { "text": { "en-US": "Generation failed" }, "error": { "en-US": "{ /error/message }" } } } ],
"finally": [ /* 常に実行 */ ] }関連ドキュメント
- 値式: 上記すべてのフィールドが従う値の規則。
- 実行セマンティクス、制約、セキュリティ: 実行順序、エラー、静的制約、セキュリティ。
- クックブック: これらの文を組み合わせた完結した例。
- Script 概要: 最上位の構造と 1 回の実行に与えられる時間。
