Katalog Statement

Setiap elemen array statements adalah satu statement. Dokumen ini merangkum field, perilaku, dan hasil dari 25 jenis statement. Setiap posisi nilai mengikuti aturan Ekspresi nilai (referensi, literal, JsonLogic, locale map); pengecualiannya ada dua, yaitu pattern pada Regex dan key pada Cache, lihat Regex dan Cache.

Ringkasan statement

KategoritypeRingkasan satu baris
Penulisan resourceResourceCreateMembuat Content/Media (opsional publikasi)
ResourceUpdatePenggantian penuh field Content/Media (field/locale yang tidak diberikan akan dihapus)
ResourcePatchPenggabungan sebagian field Content/Media (hanya field/locale yang ditentukan; null literal menghapus)
ResourceDeleteMenghapus (hanya Draft/Archived; jika Published, batalkan publikasi dulu)
ResourcePublish / ResourceUnpublishPublikasikan / batalkan publikasi
ResourceArchive / ResourceUnarchiveArsipkan / batalkan pengarsipan
Pembacaan resourceResourceReadMembaca satu item berdasarkan id
ResourceFindSatu item pertama yang cocok berdasarkan filter (null jika tidak ada)
ResourceForEachMenelusuri resource yang cocok dengan filter secara internal dan menjalankan onEach untuk tiap item
ResourceCountHanya menghitung jumlah yang cocok dengan filter (item tidak dibaca)
EksternalHttpPanggilan HTTP eksternal ({ status, body })
EmailSendMengirim 1 email melalui EmailAccount terdaftar
VariabelSetVarMendeklarasikan/memperbarui variabel berlingkup script
CacheCacheMembaca/menulis/menghapus cache berumur pendek milik Script itu sendiri
Parsing nilaiParseJsonMem-parsing teks JSON menjadi nilai (objek, array, skalar) lalu mengikatnya
Tanda tangan dan teksSignatureMemverifikasi apakah kode tanda tangan yang diterima sama dengan kode yang dibuat memakai kunci rahasia (Boolean)
HashMenghitung digest tanpa kunci (string)
RegexMenerapkan ekspresi reguler. Kecocokan (Boolean) atau grup tangkapan (array)
Alur kontrolIfPercabangan kondisional
LoopPerulangan (foreach / while / counted)
ParallelMenjalankan cabang secara bersamaan
ReturnMengembalikan hasil dan berhenti lebih awal
TryPenanganan pengecualian (catch/finally)

Statement Content yang tidak menunjuk targetnya dengan id wajib menuliskan Content Type yang ditanganinya. Pada ResourceFind·ResourceForEach·ResourceCount, contentType bersifat wajib bila resource bernilai "Content". Tidak ada kueri Content yang melintasi seluruh Space. ResourceCreate pun menuliskan Content Type yang akan dibuat. Media tidak memikul cakupan karena seluruh Space adalah satu set, sedangkan statement yang menunjuk targetnya dengan id (ResourceRead·ResourceUpdate·ResourcePatch·ResourceDelete serta statement publikasi dan pengarsipan) memiliki target sehingga tidak memerlukan cakupan.

Panggilan siklik dibatasi maksimal 3 kali. Jika Anda mengaktifkan propagateEvents (nilai bawaannya mati) pada statement penulisan resource di atas (ResourceCreate·ResourceUpdate·ResourcePublish, dan sejenisnya), penulisan itu memicu peristiwa perubahan, dan peristiwa tersebut dapat menjalankan Script lagi melalui Webhook. Rantai seperti ini (Script → peristiwa → Webhook → Script → …) berlanjut paling banyak 3 kali. Setelah itu, platform menghentikannya secara otomatis untuk mencegah perulangan tak terhingga.

Field umum

{ "type": "<StatementType>", "name": "<opsional, unik dalam script>", /* ...field khusus per tipe... */ }
  • type: diskriminator. Salah satu nilai dari tabel di atas (wajib).
  • name: opsional. Jika disertakan, hasilnya diikat ke konteks pada /<name>, sehingga statement berikutnya dapat mereferensikannya sebagai { /<name>/... }. Hilangkan jika hasilnya tidak digunakan.
  • Aturan nama binding: name merupakan key yang langsung diletakkan pada root konteks, sehingga divalidasi saat disimpan. Nama hanya boleh memakai huruf Latin, angka, _, dan - (harus dapat dipakai sebagai key JSON Pointer, sehingga karakter lain maupun nama kosong akan ditolak), tidak boleh sama dengan root yang dicadangkan (payload, rawPayload, headers, vars, error, now), dan harus unik dalam satu Script. Jika formatnya dilanggar, kata cadangan dipakai, atau ada duplikat, penyimpanan ditolak.

Bentuk referensi entitas

Referensi entitas seperti contentType dan target diseragamkan menjadi satu bentuk: { "sys": { "id": <ekspresi nilai> } }. Hanya sys.id yang diperlukan, dan tipe target disimpulkan dari resource (sys.type dan sys.targetType dihilangkan).

  • contentType.sys.id biasanya berupa literal (mis. "ct_post").
  • target.sys.id biasanya berupa ekspresi nilai { /ptr } (diselesaikan saat runtime; mis. { /payload/sys/id }).

resource

Statement keluarga resource menentukan jenis target dengan resource: "Content" | "ContentType" | "Media" | "ServiceUser".

Content Type hanya diterima oleh ResourceCount. Jika Anda menuliskannya pada statement lain, penyimpanan ditolak. Membuat atau mengubah formulirnya sendiri adalah urusan CMA, bukan Script.

ServiceUser (anggota yang mendaftar ke produk) bersifat hanya-baca. Hanya tiga statement pembacaan (ResourceRead·ResourceFind·ResourceForEach) yang menerima nilai ini; jika Anda menuliskan nilai itu pada statement penulisan, penyimpanan ditolak (lihat Error). Aturannya dibahas di Membaca direktori anggota.

Penulisan resource

Setiap statement penulisan memiliki propagateEvents (default false). Jika disetel true, penulisan tersebut memicu peristiwa perubahan sehingga tindakan lanjutan seperti Webhook dijalankan. Default-nya tidak memicu apa pun (penulisan sistem yang senyap).

ResourceCreate

Membuat Content atau Media. Content dan Media berbagi model fields, dan nilainya berupa locale map.

FieldBerlaku untukDeskripsi
resourceUmum"Content" atau "Media" (wajib)
contentTypeContentContent Type yang akan dibuat ({ sys: { id } }). Wajib untuk Content
fieldsUmumMap field { "<field>": { "<locale>": nilai } }. Setiap field yang diisi memerlukan bucket locale default. Kunci Content mengikuti definisi Content Type, dan kunci Media bersifat tetap (title, description, file)
localeUmum(kemudahan) Jika diberikan, setiap nilai dalam fields otomatis dibungkus menjadi { <locale>: nilai }
publishUmumPublikasikan setelah penulisan (tampil di CDA/ACDA). Default true
  • file dari Media: nilai fields.file.{locale} adalah instruksi ingest { "source": <ekspresi nilai>, "encoding": "url"|"base64" } (keduanya wajib). Pada penulisan yang menyertakan file, engine melakukan penyerapan itu (jika url, ia mengunduh; jika base64, ia mendekode lalu mengunggah dan memprosesnya). Penyerapan ini tidak mendeklarasikan waktu sehingga diambil dari anggaran dasar 30 detik (Anggaran waktu), dan tidak dihitung ke batas panggilan eksternal. Anda juga dapat membuat Media tanpa file (fileless). Jika publish:true tetapi tidak ada file atau pemrosesan belum selesai, tahap publikasi menghasilkan error; jika publish:false, statusnya tetap Draft.
  • Hasil (binding name): resource yang dibuat. { /<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 adalah instruksi ingest
{ "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

Mengganti sepenuhnya field dari Content atau Media target (PUT). Apa pun yang Anda berikan di fields menjadi kumpulan field yang baru, dan field serta locale yang tidak ada di sini akan dihapus. Untuk mengubah sebagian saja, gunakan ResourcePatch.

FieldDeskripsi
resource"Content" atau "Media"
targetTarget ({ sys: { id } }, wajib). id biasanya berupa { /ptr }
fieldsSeluruh kumpulan field yang akan ditulis. Nilainya berupa locale map. Karena ini adalah penggantian penuh, field dan locale yang tidak ada di sini akan dihapus. file Media adalah instruksi ingest (lihat ResourceCreate di atas). File yang tercantum selalu di-ingest ulang, dan file untuk locale yang tidak diberikan akan dihapus
locale(kemudahan) Membungkus fields secara otomatis
version(opsional) Ekspresi nilai (Int). Penguncian optimistis. Jika diberikan, pembaruan hanya berjalan jika cocok dengan sys.version target saat ini; jika tidak cocok, dibatalkan dengan error konflik versi (dapat ditangkap dengan Try). Jika dihilangkan, tidak ada pemeriksaan (last-write-wins)
publishPublikasikan ulang setelah pembaruan. Default true

Jika Anda menggunakan Update hanya untuk mengubah metadata Media, file tidak disertakan sehingga semua file terhapus (karena ini penggantian penuh). Untuk perubahan sebagian, selalu gunakan ResourcePatch.

{ "type": "ResourceUpdate", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
  "fields": { "title": { "en-US": "Hello", "ko-KR": "안녕" }, "status": { "en-US": "published" } } }

ResourcePatch

Menggabungkan sebagian field dari Content atau Media target (PATCH). Hanya menimpa field (dan locale di dalamnya) yang Anda berikan di fields, dan membiarkan field serta locale yang tidak disebutkan tetap apa adanya. Bentuk nilai, locale, version, dan publish sama seperti ResourceUpdate.

FieldDeskripsi
resource"Content" atau "Media"
targetTarget ({ sys: { id } }, wajib). id biasanya berupa { /ptr }
fieldsField yang akan ditimpa. Nilainya berupa locale map. Memperbarui hanya field dan bucket locale yang ditentukan (sisanya dipertahankan). Jika nilainya null literal, (field, locale) tersebut dihapus. file Media adalah instruksi ingest (lihat ResourceCreate di atas)
locale(kemudahan) Membungkus fields secara otomatis
version(opsional) Sama seperti ResourceUpdate (penguncian optimistis)
publishPublikasikan ulang setelah pembaruan. Default true
  • Menghapus locale atau file tertentu: berikan null literal sebagai nilai. Misalnya: "title": { "fr-FR": null } (menghapus judul fr-FR), "file": { "en-US": null } (menghapus file en-US). Ekspresi nilai yang dievaluasi menjadi null saat runtime bukanlah penghapusan, melainkan error (hanya null literal yang menghapus).
  • Jika Anda memberikan instruksi ingest pada file Media, file untuk locale tersebut akan diganti. Jika file tidak diberikan, file tetap dipertahankan.
// +1 hanya pada viewCount(en-US). title, locale lain, dan sisanya tetap dipertahankan
{ "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
  "fields": { "viewCount": { "en-US": { "$+": [ "{ /payload/fields/viewCount }", 1 ] } } } }

ResourceDelete

Menghapus target. Hanya status Draft dan Archived yang dapat dihapus. Jika Published atau Changed, penghapusan ditolak, sehingga Anda harus ResourceUnpublish terlebih dahulu. Untuk Media, penghapusan ditolak selama file Media tersebut sedang diproses. Ini tidak melakukan auto-unpublish (sama seperti CMA/ACMA).

FieldDeskripsi
resource"Content" atau "Media"
targetTarget ({ sys: { id } }, wajib)
{ "type": "ResourceDelete", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }

ResourcePublish, ResourceUnpublish, ResourceArchive, ResourceUnarchive

Mengontrol status publikasi dan pengarsipan target secara independen. Keempatnya memiliki field yang sama. Prasyarat status untuk setiap operasi sama seperti CMA/ACMA. ResourcePublish tidak dapat dilakukan dari Archived dan menuntut pemrosesan file sudah selesai. ResourceUnpublish hanya dapat dilakukan dari Published dan Changed, ResourceArchive hanya dari Draft, dan ResourceUnarchive hanya dari Archived.

FieldDeskripsi
resource"Content" atau "Media"
targetTarget ({ sys: { id } }, wajib)
version(opsional) Ekspresi nilai (Int). Penguncian optimistis. Jika diberikan, operasi hanya berjalan jika cocok dengan sys.version saat ini
{ "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 }" } } }

Pembacaan resource

ResourceRead dan ResourceFind membaca resource lalu mengikatnya sebagai nilai, sedangkan ResourceCount hanya menghitung jumlahnya. Ketiganya tidak mengubah status (tidak ada propagateEvents). Pembacaan pada ResourceForEach sendiri juga bersifat baca, tetapi jika onEach memuat statement penulisan resource, penulisan itu dijalankan untuk tiap item sehingga statusnya berubah.

Keempat statement (ResourceRead·ResourceFind·ResourceForEach·ResourceCount) menentukan salinan tersimpan mana yang dibaca melalui from (default Current). Current adalah draft terbaru yang dilihat oleh Content Studio (nilai yang dibaca CMA/ACMA), sedangkan Published adalah snapshot terpublikasi (nilai pada saat publikasi terakhir yang dikirimkan CDA/ACDA). (ServiceUser tidak dipublikasikan sehingga hanya menerima Current. Lihat Membaca direktori anggota.)

Selain itu, ResourceFind·ResourceForEach·ResourceCount mengaktifkan dan menonaktifkan Pencarian Lanjutan (Advanced Search) melalui advanced (default true). Jika tidak dituliskan, ia aktif. Ini khusus Content, sehingga diabaikan untuk pembacaan Media dan ServiceUser. Saat aktif, where dapat menggunakan operator regex, near, dan within serta pencarian teks lengkap (pada field LongText yang pencarian teks lengkapnya aktif, eq juga menemukan item yang mengandung nilai tersebut, dengan pencocokan parsial atau serupa), dan pengurutan fields.* dapat dipakai. Saat tidak aktif, ketiga operator itu ditolak, eq pada teks menjadi pencocokan persis, dan prefix serta operator perbandingan dan daftar tetap berfungsi terlepas dari Pencarian Lanjutan. Item yang baru dibuat atau diubah memerlukan waktu sesaat (sekitar 1 detik) untuk tercermin di pencarian lanjutan, sehingga bisa terlewat oleh kueri pencarian lanjutan yang langsung menyusul. Karena bawaannya aktif, jeda ini berlaku untuk semua kueri kecuali Anda menyetel advanced menjadi false. Untuk membaca item yang baru ditulis dengan segera, gunakan ResourceRead berdasarkan id (salinan tersimpan default, tanpa jeda) atau lakukan kueri berdasarkan sys.id yang dikembalikan oleh penulisan.

createdBy: ":self" pada where berarti "hanya yang dibuat oleh pemanggil saat ini". Namun ini tidak dapat dipakai pada Script yang mengizinkan panggilan anonim (anonymousCallEnabled). Pada kasus itu :self di-resolve menjadi penulis, bukan pemanggil, sehingga resource milik penulis terbuka secara senyap; karena itu definisi semacam itu ditolak saat disimpan (lihat Panggilan anonim).

Di where dan order, field dari Content ditulis sebagai fields.<field> (nama field saja tidak dikenali). Pada fields.<field>, mesin secara otomatis menerapkan locale default Space, sehingga Anda tidak menambahkan locale secara langsung. fields.status dan fields.slug pada contoh di bawah sudah merupakan kueri locale default. Hanya ketika ingin melakukan kueri pada locale tertentu (bukan default), tentukan secara eksplisit sebagai fields.<field>.<locale> (mis. fields.title.ko-KR). sys.* (mis. sys.createdAt) dan createdBy (:self) ditulis apa adanya tanpa fields.. Aturan selengkapnya ada di locale where·order pada Ekspresi nilai.

Membaca direktori anggota (ServiceUser)

ResourceRead·ResourceFind·ResourceForEach menerima "ServiceUser" pada resource untuk membaca direktori anggota Space tersebut (ResourceCount tidak menerimanya, lihat ResourceCount di bawah). Gunakan untuk memastikan siapa pemilik sebuah pesanan, atau untuk alur yang mencari anggota berdasarkan email lalu meneruskan sys.id-nya ke statement berikutnya. Aturan berikut berlaku untuk ketiga statement itu.

  • Hanya bisa dibaca. ResourceCreate·ResourceUpdate·ResourcePatch·ResourceDelete serta statement publikasi dan pengarsipan tidak menerima "ServiceUser", dan definisi semacam itu ditolak saat disimpan. Ini bukan sesuatu yang bisa dibuka dengan menambah izin: di Script memang tidak ada jalan untuk mengubah anggota, sehingga penolakannya bukan error izin melainkan statement yang salah tulis.
  • Penyimpanan hanya berhasil jika penulis memiliki izin direktori anggota. Pemeriksaannya tidak lewat map izin seperti Content dan Media, melainkan melihat apakah settings pada SpaceRole penulis memuat SETTING_SERVICE_LOGIN (atau SETTING_ALL). Alasannya, direktori anggota adalah resource yang dikelola pengaturan Space di semua jalur lainnya juga. Jika tidak ada, penyimpanan ditolak (lihat Model keamanan).
  • from hanya menerima Current. Anggota bukan resource yang dipublikasikan, sehingga memberi Published membuat eksekusi gagal.
  • contentType dan advanced diabaikan. Direktori anggota tidak terbagi per Content Type (satu set untuk seluruh Space), dan Pencarian Lanjutan pun khusus Content.
  • sys.email pada where hanya menerima operator keluarga kecocokan persis (eq·ne·in·nin). Alamat anggota disimpan terenkripsi, sehingga perbandingan urutan atau prefix tidak bermakna. Jika Anda memberi operator lain, alih-alih mengembalikan 0 hasil secara senyap, eksekusinya gagal.
  • Hasilnya adalah resource ServiceUser itu sendiri. Rujuk seperti { /<name>/sys/id } dan { /<name>/nickname }. Strukturnya dibahas di referensi ServiceUser. Ketika mengirim email kepada anggota yang ditemukan, jangan mengeluarkan alamatnya, melainkan berikan sys.id-nya pada toServiceUser milik EmailSend (engine me-resolve alamat tepat sebelum pengiriman sehingga alamat anggota tidak masuk ke ruang variabel Script).
// Mencari satu anggota berdasarkan email. null jika tidak ada
{ "type": "ResourceFind", "resource": "ServiceUser",
  "where": { "sys.email": { "eq": "{ /payload/fields/email }" } }, "name": "member" }

ResourceRead

Membaca satu item berdasarkan id (get-by-id). Hasilnya mengikat seluruh resource ke nama.

FieldDeskripsi
resource"Content"·"Media"·"ServiceUser"
targetTarget ({ sys: { id } }). id berupa ekspresi nilai
from(opsional) Current (default, draft terbaru) atau Published (snapshot terpublikasi). ServiceUser hanya Current
  • Hasil: yang diikat adalah resource itu sendiri. Jika statement ini diberi name, rujuk langsung dengan { /<name>/sys/id } dan { /<name>/fields/<field>/<locale> } (dengan "name": "order" pada contoh di bawah, berarti { /order/sys/id }). Ini bukan list, jadi tidak melewati indeks array.
  • Jika target tidak ada, akan terjadi error. Anda dapat menanganinya dengan membungkusnya dalam Try.
{ "type": "ResourceRead", "resource": "Content",
  "target": { "sys": { "id": "{ /payload/fields/orderId }" } }, "name": "order" }

ResourceFind

Membaca satu item pertama yang cocok dengan filter. Jika tidak ada, hasilnya null. Gunakan untuk menemukan satu record berdasarkan kunci bisnis unik (slug, email, sku).

FieldDeskripsi
resource"Content"·"Media"·"ServiceUser"
contentTypeContent Type yang menjadi cakupan pencarian ({ sys: { id } }). Wajib untuk Content. Diabaikan pada Media dan ServiceUser
whereFilter ({ "<field>": { "<op>": <nilai> } }). Operator yang tersedia mengikuti daftar operator (regex/near/within memerlukan advanced). Mendukung createdBy: ":self". Untuk sys.email milik ServiceUser, hanya eq·ne·in·nin (Membaca direktori anggota)
orderUrutan yang menentukan "yang pertama" ketika beberapa item cocok (mis. "-sys.createdAt")
from(opsional) Current (default, draft terbaru) atau Published (snapshot terpublikasi). ServiceUser hanya Current
advanced(opsional) Jalankan lewat Pencarian Lanjutan (Advanced Search). Hanya Content (Media·ServiceUser diabaikan). Default true. Lihat catatan Pembacaan resource di atas.
  • Hasil: mengikat resource yang pertama cocok ke name statement ini. Referensikan secara langsung dengan { /<name>/fields/<field>/<locale> }. Karena bernilai null saat tidak ada, bercabanglah berdasarkan keberadaannya dengan { "==": [ "{ /<name> }", null ] } (pola find-then-upsert yang umum).
{ "type": "ResourceFind", "resource": "Content", "contentType": { "sys": { "id": "ct_article" } },
  "where": { "fields.slug": { "eq": "{ /payload/fields/slug }" } }, "name": "found" }

ResourceForEach

Menelusuri resource yang cocok dengan filter secara internal dan menjalankan onEach untuk tiap item. Ini adalah statement untuk melakukan pekerjaan pada tiap item, bukan untuk membentuk koleksi yang akan dipakai sebagai nilai. Gunakan untuk pekerjaan berulang seperti mempublikasikan draft secara massal, mengubah Content yang memenuhi kondisi secara massal, atau mengirim/menyinkronkan tiap item ke luar. Untuk membaca satu item saja, gunakan ResourceRead (id) atau ResourceFind (filter).

FieldDeskripsi
resource"Content"·"Media"·"ServiceUser" (wajib)
contentTypeContent Type yang menjadi cakupan penelusuran ({ sys: { id } }). Wajib untuk Content. Diabaikan pada Media dan ServiceUser
whereFilter ({ "<field>": { "<op>": <nilai> } }). Maknanya sama dengan where pada ResourceFind (batasan sys.email milik ServiceUser juga sama). Operator yang tersedia mengikuti daftar operator (regex/near/within memerlukan advanced). Mendukung createdBy: ":self"
orderUrutan (mis. "sys.createdAt,sys.id"). Jika tidak ada, urutan bawaan platform
fromCurrent (default, draft terbaru) atau Published (snapshot terpublikasi). ServiceUser hanya Current
advancedTelusuri lewat Pencarian Lanjutan (Advanced Search). Hanya Content (Media·ServiceUser diabaikan). Default true. Lihat catatan Pembacaan resource di atas
limit(opsional, 1 atau lebih) Batas atas jumlah total yang diproses (bukan ukuran halaman). Jika tidak ada, menelusuri hingga batas atas platform (10.000 item)
name(opsional) Nama untuk mengikat item saat ini. Diikat ulang setiap iterasi dan direferensikan sebagai { /<name> } di dalam onEach (masa hidup sama seperti name pada Loop; setelah penelusuran selesai, item terakhir tetap terikat). Hilangkan jika Anda tidak mereferensikan item
onEachArray statement anak yang dijalankan untuk tiap item (wajib)
  • Tidak mengikat koleksi (foreach, bukan map). Tidak ada { items, next } maupun cursor. Alih-alih mengembalikan hasil penelusuran sebagai nilai, ia menjalankan onEach untuk tiap item. Jika Anda memerlukan daftarnya, kumpulkan sendiri dengan SetVar. Jika yang Anda perlukan hanya jumlahnya, gunakan ResourceCount.
  • Tanpa limit pun bukan berarti penelusuran tak terhingga. Jika tidak ada, ia menelusuri hingga batas atas platform (10.000 item), dan gagal jika mencapai batas itu sementara masih ada kecocokan tersisa (agar tidak melaporkan keberhasilan padahal ada item yang belum tersentuh). Sebaliknya, mencapai limit yang Anda deklarasikan adalah penghentian yang disengaja sehingga berakhir normal. limit yang melebihi batas atas ditolak saat disimpan.
  • Tidak ada cursor. Jika menyelesaikan seluruhnya, berarti berhasil; jika terputus di tengah (batas wall-clock atau kuota terlampaui, kegagalan onEach yang tidak tertangani), berarti gagal, dan error menunjuk pada item mana serta mengapa gagal. Pelanjutan diekspresikan penulis dengan datanya sendiri (jadikan where bermakna "belum diproses" dan tandai penyelesaian di akhir onEach, sehingga eksekusi ulang melanjutkan dari sisa yang ada).
  • Pada anggaran waktu ia dihitung sebagai perkalian. Waktu yang dideklarasikan statement ini adalah waktu yang dideklarasikan onEach dikalikan jumlah item yang diproses (limit, atau 10.000 jika tidak ada) (Anggaran waktu). Karena ini statement komposit yang memiliki anak, statement itu sendiri tidak dihitung ke anggaran leaf panggilan eksternal; statement panggilan eksternal di dalam onEach-lah yang dihitung ke anggaran.
  • onEach dapat memuat panggilan eksternal (Http·EmailSend) atau penyerapan file Media seperti statement lainnya (sama seperti body pada Loop). Memproses hasil kueri resource satu kali per item adalah alasan keberadaan statement ini.
// Temukan semua postingan berstatus draft lalu publikasikan tiap item
{ "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

Hanya menghitung jumlah yang cocok dengan filter. Karena ia tidak membaca itemnya, gunakan statement ini saat yang Anda perlukan adalah jumlahnya, bukan daftarnya. Ini tempatnya untuk memeriksa sisa stok, menentukan apakah nilai yang sama sudah ada, atau memeriksa apakah suatu batas sudah terlampaui.

FieldDeskripsi
resource"Content" atau "ContentType" (wajib). Media dan ServiceUser tidak dapat dihitung, dan menuliskannya seperti itu membuat penyimpanan ditolak
contentTypeContent Type yang menjadi cakupan penghitungan ({ sys: { id } }). Wajib untuk Content. Diabaikan ketika yang dihitung adalah Content Type (seluruh Space adalah satu set)
whereFilter. Maknanya sama dengan where pada ResourceFind. Semua item yang cocok ikut dihitung
from(opsional) Current (default, draft terbaru) atau Published (snapshot terpublikasi)
advanced(opsional) Jalankan lewat Pencarian Lanjutan (Advanced Search). Hanya Content (diabaikan ketika yang dihitung adalah Content Type). Default true. Lihat catatan Pembacaan resource di atas
name(opsional) Nama untuk mengikat jumlahnya
  • Hasil: mengikat jumlah yang cocok ke name. Rujuk dengan { /<name> } lalu gunakan untuk perbandingan dan percabangan.
  • Ia tidak mengembalikan itemnya. Jika Anda memerlukan itemnya, gunakan ResourceFind (satu item pertama yang cocok) atau ResourceForEach (dijalankan untuk tiap item).
  • Jangan menghitung dengan menelusuri ResourceForEach hanya demi memperoleh jumlahnya. Penelusuran mengambil anggaran waktu sebanyak perkalian dengan jumlah item (Anggaran waktu), dan gagal jika mencapai batas atas platform sementara masih ada kecocokan tersisa. Jika yang Anda butuhkan hanya hitungannya, statement ini menyelesaikannya sekali jalan.
  • Tidak ada order dan limit. Menghitung tidak memerlukan urutan, dan semua yang cocok memang dihitung.
// Menghitung berapa banyak komentar yang tertaut pada postingan ini
{ "type": "ResourceCount", "resource": "Content", "contentType": { "sys": { "id": "ct_comment" } },
  "where": { "fields.postId": { "eq": "{ /payload/sys/id }" } }, "name": "commentCount" }

Eksternal

Http

Memanggil HTTP eksternal. Karena ini panggilan eksternal, ia dihitung ke batas panggilan eksternal per paket, dan pada Anggaran waktu ia dihitung sebagai timeoutMs (atau 30 detik jika tidak ada) × (1 + retry).

FieldDeskripsi
method"GET", "POST", "PUT", "PATCH", "DELETE"
urlURL target (ekspresi nilai; { /ptr } dapat disisipkan)
headers[{ "key", "value", "secret"? }]. value berupa ekspresi nilai. Header dengan secret:true diperlakukan sebagai khusus CMA (administrator): tidak terekspos ke pengguna akhir dan hanya didekripsi tepat sebelum dikirim. Jika Content-Type diletakkan di sini, body diserialisasi ke format tersebut (di bawah)
bodyBody permintaan (ekspresi nilai atau JSON). Format pengirimannya ditentukan oleh header Content-Type
timeoutMsBatas waktu untuk panggilan ini (ms)
retryJumlah percobaan ulang ketika status respons 400 atau lebih. Default 0; batas atasnya 2
ignoreStatusCodeApakah panggilan ini dianggap sebagai kegagalan ketika status akhir (setelah percobaan ulang) 400 atau lebih. Jika false (default), panggilan ini diperlakukan sebagai kegagalan dan menjadi target Try/catch. Jika true, panggilan tidak dianggap sebagai kegagalan dan { status, body } diikat apa adanya (pemanggil bercabang sendiri berdasarkan status)
responseTypeBentuk penerimaan isi respons. "Json" (default) mem-parsingnya menjadi objek atau array, "Text" menerimanya sebagai string
  • Hasil: { status, body }. Jika statement ini diberi name, { /<name>/status }, { /<name>/body/... }. Bentuk body ditentukan oleh responseType.
  • responseType hanya berlaku untuk respons yang berhasil. Isi respons dengan status 400 atau lebih diikat untuk keperluan diagnosis, apa pun nilai yang dideklarasikan (nilai hasil parsing jika JSON, string jika bukan).
  • Jika "Json" tetapi isinya bukan JSON, panggilan ini gagal (menjadi target Try/catch). Untuk API yang tidak mengembalikan JSON, terima responsnya sebagai "Text", lalu parsing dengan ParseJson bila perlu diperlakukan sebagai nilai.
  • "Text" didekode dengan charset dari Content-Type respons, dan dianggap UTF-8 bila tidak ada charset. Bila isinya kosong, body bernilai null pada kedua kasus.
  • Batas ukuran respons: Body respons berukuran maksimum 10MiB. Jika melebihi itu, panggilan ini gagal dengan pengecualian dan dapat ditangani seperti kegagalan runtime lainnya dengan Try/catch (ini adalah kegagalan berbasis ukuran, jadi tidak diabaikan oleh 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" }

Dalam format apa body dikirim

Content-Type yang diletakkan pada headers menentukan format serialisasi body. Perbandingannya mengabaikan besar-kecil huruf dan parameter seperti ;charset=…, lalu hanya melihat bagian depannya. Jika headernya tidak ada atau nilainya kosong, permintaan dikirim sebagai application/json. Header ini hanya dilekatkan ketika ada body, jadi bila tidak ada body, header yang dituliskan dikirim apa adanya. Jika key yang sama diletakkan beberapa kali, hanya nilai pertama yang dipakai dan semuanya digabung menjadi satu.

body yang tidak dapat dimuat dalam format yang dideklarasikan akan dikoreksi ke format yang dapat memuatnya sebelum dikirim. Header tidak pernah menyatakan hal yang berbeda dari body yang sebenarnya.

Berikut kombinasi yang dikirim sesuai nilai yang dideklarasikan.

Content-Type yang dideklarasikanBentuk bodyBody yang dikirim
application/jsonApa punJSON
application/x-www-form-urlencodedObjek atau arrayorder[id]=A-2481&order[amount]=34000
text/plainSkalarNilai apa adanya
Lainnya (text/xml dll.)Apa punJSON

Berikut kombinasi yang dikoreksi karena tidak dapat dimuat dalam format yang dideklarasikan.

Content-Type yang dideklarasikanBentuk bodyContent-Type yang benar-benar dikirimBody yang dikirim
application/x-www-form-urlencodedSkalartext/plain;charset=UTF-8Nilai apa adanya
text/plainObjek atau arrayapplication/jsonJSON

Dua baris ini menjelaskan bagaimana permintaan dikirim ketika pasangannya tidak cocok, dan bukan cara untuk memperoleh format yang Anda maksudkan. Jika body disusun dengan ekspresi nilai, ia dapat menjadi skalar tergantung pada payload saat eksekusi, dan koreksi ini terjadi tanpa kesalahan. Jika pihak penerima mempermasalahkan formatnya, perbaiki salah satu di antara bentuk body dan Content-Type sesuai maksud Anda.

form-urlencoded membentangkan objek menjadi key berkurung siku dan array menjadi indeks.

bodyKey dan nilai hasil pembentangan
{ "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=

Key dan nilai dikirim dengan persen-encoding UTF-8. Tabel di atas adalah bentuk hasil dekode untuk memperlihatkan struktur key-nya. Meskipun nilainya mengandung & atau +, keduanya tidak disalahartikan sebagai pemisah pasangan atau spasi dan diteruskan apa adanya.

Notasi yang membentangkan struktur bersarang menjadi key berkurung siku adalah konvensi yang dipakai luas, bukan spesifikasi dari format itu sendiri. Periksa apakah pihak penerima memulihkan order[id] menjadi objek bersarang, dan jika tidak dipulihkan, susun body dengan key yang datar.

{ "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

Mengirim 1 email melalui EmailAccount terdaftar. Field yang diterima hanya yang dipetakan langsung ke SMTP/MIME. Tidak ada id template, penjadwalan pengiriman, atau ekstensi khusus penyedia (jika membutuhkan fitur semacam itu, panggil langsung API layanan email tersebut dengan Http). Pengirim (alamat pengirim) tidak ditentukan di sini melainkan berasal dari EmailAccount yang ditunjuk account.

FieldDeskripsi
accountReferensi EmailAccount pengirim ({ sys: { id } }, wajib). Biasanya id literal. Jika diberikan sebagai ekspresi nilai, ia di-resolve saat waktu pengiriman sehingga tidak dapat diperiksa saat disimpan
toAlamat penerima (ekspresi nilai). Gunakan tepat salah satu dari to atau toServiceUser
toServiceUserMenentukan penerima sebagai referensi ServiceUser ({ sys: { id } }; sys.id-nya boleh berupa ekspresi nilai). Karena engine me-resolve alamat tepat sebelum pengiriman, alamat anggota tidak masuk ke ruang variabel Script
ccArray alamat penerima tembusan (ekspresi nilai)
bccArray alamat penerima tersembunyi (ekspresi nilai)
subjectSubjek (ekspresi nilai, wajib)
bodyBody (ekspresi nilai, wajib). Selalu dikirim sebagai text/html sehingga gunakan markup, bukan teks biasa (baris baru menjadi spasi, < diinterpretasikan sebagai tag). Hasil ekspresi nilai yang diinterpolasi akan di-escape HTML
replyTo(opsional) Header Reply-To (ekspresi nilai). Bisa berbeda dari pengirim (mis. mengirim dari no-reply tetapi balasan menuju alamat dukungan)
timeoutMs(opsional, 1 atau lebih) Batas waktu pengiriman ini (ms). Jika tidak ada, nilai bawaan platform; nilai yang melebihi batas atas ditolak saat disimpan
  • Total penerima maksimum 50 orang. Dihitung dengan menjumlahkan to (1 orang), cc, dan bcc (envelope SMTP tidak membedakan cc/bcc dan semuanya keluar sebagai penerima, jadi dihitung sebagai total). Jika melebihi, ditolak saat disimpan dan dijalankan. Untuk mengirim ke banyak orang, kirim 1 email per item dengan ResourceForEach + EmailSend.
  • Tidak mengikat hasil. Keberhasilan hanya berarti "penyedia menerima email", sehingga tidak ada nilai yang dikembalikan dan name tidak diterima. Juga tidak melakukan percobaan ulang (email tidak idempoten, sehingga percobaan ulang setelah kegagalan yang ambigu akan menyebabkan pengiriman ganda. Karena itu ia tidak mengikuti retry milik Http). Kegagalan di-throw dan ditangani oleh catch pada Try.
  • Ini panggilan eksternal. Ia dihitung ke batas panggilan eksternal per paket, dan pada Anggaran waktu ia dihitung sebagai timeoutMs (atau 10 detik jika tidak ada) satu kali (karena tidak melakukan percobaan ulang, hitungannya tidak dikalikan seperti pada Http). Dapat dipakai di dalam onEach milik ResourceForEach (bentuk standar untuk pengiriman banyak email).
{ "type": "EmailSend", "account": { "sys": { "id": "eml_orders" } },
  "to": "{ /order/fields/email/en-US }",
  "subject": "Pesanan berhasil diterima (nomor pesanan { /order/sys/id })",
  "body": "<p>Pesanan Anda telah diterima. Kami akan memberi tahu lagi ketika pengiriman dimulai.</p>",
  "replyTo": "support@my-shop.example" }

Variabel

SetVar

Mendeklarasikan atau memperbarui variabel mutable berlingkup script. Referensikan dengan { /vars/<var> } (JsonLogic tidak memiliki deklarasi variabel, jadi ini disediakan sebagai statement).

FieldDeskripsi
varNama variabel. Direferensikan dengan { /vars/<var> }
valueEkspresi nilai. Dapat mereferensikan dirinya sendiri untuk berakumulasi
{ "type": "SetVar", "var": "total", "value": 0 }
{ "type": "SetVar", "var": "total", "value": { "$+": [ "{ /vars/total }", "{ /row/qty }" ] } }   // akumulasi
{ "type": "SetVar", "var": "ids",   "value": { "$merge": [ "{ /vars/ids }", [ "{ /row/sys/id }" ] ] } }  // kumpulkan ke array

Cache

Cache

Membaca dan menulis cache berumur pendek yang hanya dimiliki Script itu sendiri. Ini tempat untuk menahan selama beberapa detik nilai yang sayang diambil ulang setiap kali, seperti hasil panggilan eksternal, lalu memakainya kembali pada pemanggilan berikutnya. Karena ini bukan panggilan eksternal, ia tidak ikut dihitung dalam jumlah panggilan eksternal per definisi, dan tidak mendeklarasikan waktu apa pun pada anggaran waktu.

FieldDeskripsi
actionSalah satu dari "Set" (menulis), "Get" (membaca), atau "Delete" (menghapus) (wajib)
keyKunci cache (Cache Key) (wajib). Ini literal, bukan ekspresi nilai (lihat di bawah). Maksimal 128 karakter; jika melebihi, penyimpanan ditolak
valueNilai yang akan disimpan (khusus Set)
ttlLama cache bertahan (khusus Set, dalam detik). Antara 1 dan 30; jika dihilangkan, nilainya 5
defaultValueNilai yang diikat Get ketika tidak ada data yang tersimpan di cache (khusus Get). Jika dihilangkan, nilainya null
nameNama yang menampung hasil. Untuk Get bersifat wajib (jika nilai yang dibaca tidak punya tempat tujuan, tidak ada alasan untuk membacanya). Set mengikat nilai yang disimpan dan Delete mengikat apakah penghapusan benar-benar dilakukan; keduanya opsional
  • Tulis hanya field yang sesuai dengan operasinya. Menulis ttl pada Get atau defaultValue pada Set membuat penyimpanan ditolak.
  • Data yang tidak ada dan data yang sudah kedaluwarsa tidak dibedakan. Keduanya mengikat defaultValue. Begitu pula ketika yang tersimpan adalah null.
  • Cakupan penyimpanannya hanya satu Script itu. Script lain di Space yang sama tidak dapat melihat data milik satu sama lain meskipun memakai kunci cache yang sama. Jika Script itu diubah atau dihapus, seluruh data milik Script itu ikut hilang.
  • key bersifat literal. Jika data boleh dipilih dengan kunci cache yang datang dari permintaan, pemanggil yang menentukan apa yang dibaca, dan Script yang menyimpan satu nilai untuk tiap anggota akan menyerahkan nilai milik seorang anggota kepada anggota lain. Karena itu, jika key memuat { /pointer }, ia tidak diubah menjadi nilai dan tidak juga dipakai secara harfiah. Penyimpanannya sendirilah yang ditolak.
  • Tidak boleh diletakkan di dalam perulangan. Jika ada Cache di dalam blok Loop atau ResourceForEach, penyimpanan ditolak. Sebab batas jumlah di bawah ini tidak lagi menjadi batasan apa pun ketika berada di dalam perulangan. Karena satu data ditulis pada tiap iterasi, jumlah statement yang tertulis di definisi tidak lagi sesuai dengan jumlah kunci cache yang benar-benar dipakai.
  • Satu definisi dapat memuat paling banyak 5 (termasuk yang bersarang, dijumlahkan tanpa memandang operasinya). Jika melebihi, penyimpanan ditolak.
  • Nilai yang disimpan maksimal 10.240 byte (10KiB). Jika melebihi, statement itu gagal (status 422). Sama seperti kegagalan runtime lainnya sehingga dapat ditangani secara lokal dengan Try/catch.
// Memakai ulang kurs selama 30 detik.
{ "type": "Cache", "action": "Get", "name": "cached", "key": "rates" }
 
// Jika ada nilai yang sedang disimpan, kembalikan apa adanya tanpa panggilan eksternal
{ "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 }" }
 
// Membuang nilai yang sedang disimpan sebelum kedaluwarsa
{ "type": "Cache", "action": "Delete", "key": "rates" }

Parsing nilai

ParseJson

Mem-parsing teks JSON menjadi nilai yang diwakilinya lalu mengikatnya ke sebuah nama. Dipakai untuk isi yang diterima dari Http dengan responseType: "Text", string JSON yang masuk lewat payload, atau JSON yang disimpan sebagai string di sebuah field. Karena ini bukan panggilan eksternal, ia tidak dihitung ke batas panggilan eksternal, dan tidak mendeklarasikan waktu apa pun pada anggaran waktu.

FieldDeskripsi
nameNama yang menampung hasil parsing (wajib). Pada statement lain bersifat opsional, tetapi di sini wajib. Statement ini tidak melakukan apa pun selain mengikat hasilnya, jadi tanpa nama ia menjadi statement tanpa efek
valueTeks JSON yang akan di-parsing (ekspresi nilai, wajib). Tunjuk nilai dari langkah sebelumnya seperti { /resp/body }, atau tulis teks JSON apa adanya sebagai literal (tanda { di dalam literal tidak dibaca sebagai template { pointer })
  • Hasil: nilai hasil parsing itu sendiri. Objek tetap objek, array tetap array, dan nilai tunggal seperti 42 atau "a" juga di-parsing. Setelah itu tunjuk bagian dalamnya dengan { /<name>/... }.
  • Nilai yang sudah di-parsing diikat apa adanya. Ketika value menghasilkan sesuatu yang bukan string, tidak ada teks untuk di-parsing, sehingga nilai itu diikat sebagaimana adanya.
  • { /pointer } di dalam teks yang di-parsing tidak diuraikan lagi. Meski string yang diterima dari luar memuat ekspresi seperti { /payload/... }, itu tidak digantikan nilai dan tetap menjadi teks.
  • null mencakup dua kasus berbeda. Jika teks yang di-parsing hanya satu kata null, itu normal dan hasilnya juga null. Sebaliknya, jika tempat yang ditunjuk value kosong sehingga tidak ada nilai sama sekali, tidak ada yang bisa di-parsing dan statement gagal.
  • Gagal: ketika value menghasilkan tanpa nilai atau hanya spasi, dan ketika teksnya bukan JSON. Tangani dengan Try/catch seperti kegagalan runtime lainnya; pesan kesalahan memuat teks yang coba di-parsing.
  • Ia dihitung sebagai satu statement pada jumlah statement per definisi, tetapi tidak berkaitan dengan batas panggilan eksternal maupun batas SetVar.
// 1) API yang tidak mengembalikan JSON: terima sebagai Text lalu parsing
{ "type": "Http", "method": "GET", "url": "https://api.partner.example/v1/quote",
  "responseType": "Text", "name": "resp" },
{ "type": "ParseJson", "name": "quote", "value": "{ /resp/body }" },
 
// 2) Parsing string JSON yang masuk lewat payload
{ "type": "ParseJson", "name": "spec", "value": "{ /payload/fields/specJson }" }

Verifikasi tanda tangan dan pemrosesan teks

Ini adalah statement untuk memeriksa tanda tangan yang dikirim penyedia pembayaran lewat webhook, dan untuk menguraikan string tempat tanda tangan itu dibungkus. Ketiganya bukan panggilan eksternal melainkan perhitungan, sehingga tidak dihitung ke batas panggilan eksternal dan tidak mendeklarasikan waktu apa pun pada anggaran waktu; karena tidak memiliki posisi data, ketiganya juga tidak berkaitan dengan aturan awalan $. Contoh lengkap yang mengombinasikan ketiganya ada di verifikasi tanda tangan webhook pada Cookbook.

Ketiga statement memiliki batas atas panjang nilai hasil resolve. Yang dibatasi bukan panjang ekspresinya, melainkan panjang nilai yang ditunjuk ekspresi itu (enam belas karakter { /rawPayload } menunjuk ke puluhan KB), dan jika melebihi batas, eksekusinya gagal sehingga dapat ditangani dengan Try. Angkanya dikumpulkan di Batas atas panjang nilai.

Signature

Memeriksa apakah kode tanda tangan yang diterima sama dengan kode yang dibuat memakai secret, lalu mengikat jawabannya sebagai nilai Boolean. Tanda tangan yang dikirim penyedia pembayaran (PG·MoR) lewat webhook diverifikasi dengan statement ini.

FieldDeskripsi
nameNama yang menampung hasil verifikasi (wajib). { /<name> } bernilai true atau false. Memverifikasi tetapi tidak memakai hasilnya sama saja dengan tidak memverifikasi, sehingga tidak boleh dihilangkan
algorithmHash yang dipakai untuk membuat kode (wajib). SHA1·SHA256·SHA384·SHA512
secretKunci rahasia yang Anda bagi dengan pihak lain (ekspresi nilai, wajib)
secretEncodingNotasi yang Anda pakai untuk menuliskan secret. Utf8 (default, kunci teks)·Hex·Base64. Membiarkan kunci yang diterbitkan dalam hex atau base64 sebagai teks membuatnya menjadi kunci lain, sehingga kode yang tampak masuk akal tetap terbentuk tetapi tidak akan pernah cocok
valuePesan yang kodenya dihitung (ekspresi nilai, wajib). Karena harus sama huruf per huruf dengan byte yang ditandatangani pihak lain, biasanya berupa { /rawPayload } atau { /rawPayload } dengan timestamp yang dikirim penyedia di header ditambahkan di depannya
expectedKode yang dikirim pemanggil (ekspresi nilai, wajib). Mis. { /headers/x-signature }
  • Hasil: Boolean. Setelah itu pakai { /<name> } apa adanya sebagai kondisi If.
  • value ditulis dengan /rawPayload, bukan /payload yang sudah di-parsing. Jika payload hasil parsing dijadikan string kembali, spasi, notasi angka, dan escape akan dinormalkan sehingga tidak kembali menjadi byte yang ditandatangani pihak lain (Root konteks).
  • Tidak ada field yang menentukan notasi keluaran. Karena algorithm mengunci panjang byte kode dan panjang string hex maupun base64 untuk panjang byte yang sama tidak bertumpang tindih, engine dapat memulihkan byte-nya tanpa diberi tahu pihak lain mengirim dalam bentuk yang mana. Huruf besar-kecil pada hex, serta base64 dan base64url (termasuk ada atau tidaknya padding), juga tidak dibedakan karena alasan yang sama.
  • Kegagalan dan false dibedakan oleh siapa yang memberi nilai itu.
    • Jika expected tidak ada atau kodenya tidak cocok, hasilnya hanya false, bukan kegagalan. Alasannya, memberi tahu ketiadaan header dan ketidakcocokan kode secara terpisah berarti mengajari pengirim bagian mana yang salah.
    • Jika value kosong, kode dihitung dengan pesan kosong. Body kosong pun adalah objek tanda tangan.
    • Jika secret tidak ada atau bukan notasi yang dideklarasikan secretEncoding, itu kegagalan. Di antara ketiganya, hanya inilah masukan penulis sendiri. secret dan value tidak disertakan dalam pesan kegagalan.
  • Batas atas value adalah 65.536 karakter (berdasarkan nilai hasil resolve). Angka ini disesuaikan dengan ukuran body webhook yang benar-benar dikirim penyedia.
  • Perbandingannya menilai kesamaan nilai secara constant-time. Jadi tidak ada kebocoran lewat waktu respons tentang berapa byte awal yang sudah cocok.
  • secret tidak disimpan terenkripsi. Berbeda dengan secret: true pada header Http (disimpan terenkripsi lalu didekripsi tepat sebelum dikirim), nilainya tetap seperti yang Anda tulis di definisi, sehingga role yang dapat membaca Script itu dapat melihat nilainya. Anggota (ServiceUser) tidak dapat membaca definisi Script (penyusunan dan pembacaannya khusus CMA).
// Penyedia yang menandatangani seluruh body
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
  "secret": "whsec_9f2c1b7ae4", "value": "{ /rawPayload }",
  "expected": "{ /headers/x-webhook-signature }" }
 
// Penyedia yang menerbitkan kunci dalam base64
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
  "secret": "aGVsbG8td2VlZ2xvbw==", "secretEncoding": "Base64",
  "value": "{ /rawPayload }", "expected": "{ /headers/webhook-signature }" }

Hash

Men-digest value lalu mengikatnya sebagai string dalam notasi yang ditentukan encoding. Dipakai untuk mereproduksi skema tanda tangan yang bukan HMAC, melainkan "menyambung beberapa field dengan kunci rahasia lalu menghitung SHA256".

FieldDeskripsi
nameNama yang menampung digest (wajib)
algorithmMD5·SHA1·SHA256·SHA384·SHA512 (wajib). MD5 ada untuk mereproduksi skema lama yang mengharuskannya, bukan nilai yang perlu Anda pilih untuk tanda tangan baru
valuePesan yang akan di-digest (ekspresi nilai, wajib)
encodingNotasi hasil. Hex (default)·HexUpper·Base64·Base64Url
  • Tidak ada field secret. Karena setiap skema menempatkan kunci di depan, di belakang, atau di tengah, menuliskan kunci langsung di dalam value justru dapat menyatakan semua posisi itu.
  • Hasil: string. Ketika membandingkannya dengan kode yang dikirim pihak lain, tulis { "==": [ "{ /<name> }", "{ /headers/... }" ] }. Perbandingan ini adalah perbandingan kesetaraan biasa, berbeda dengan perbandingan constant-time milik Signature.
  • Jika value di-resolve tanpa nilai atau hanya berisi spasi, itu kegagalan (karena ini ekspresi penulis sendiri).
  • Batas atas value adalah 128 karakter. Karena tempat ini memuat beberapa field yang disambung, batasnya jauh lebih sempit daripada Signature. Jika Anda harus menghitung atas seluruh body webhook, pakai Signature.
// SHA256(nomor pesanan + jumlah + merchantKey) sebagai hex huruf besar
{ "type": "Hash", "name": "expectedSign", "algorithm": "SHA256", "encoding": "HexUpper",
  "value": "{ /payload/orderId }{ /payload/amount }9f2c1b7ae4" }

Regex

Menerapkan pattern pada value lalu mengikat apa yang diminta mode. Karena ekspresi nilai tidak memiliki sarana untuk memotong string (hanya ada cat untuk menyambung dan in untuk melihat keterkandungan), statement ini dipakai untuk menguraikan beberapa nilai yang datang terbungkus dalam satu header, seperti t=…,v1=….

FieldDeskripsi
nameNama yang menampung hasil (wajib). Pada Capture, elemennya ditunjuk dengan { /<name>/1 }
mode"Match" mengikat kecocokan sebagai Boolean, "Capture" mengikat kecocokan pertama sebagai array (wajib)
patternEkspresi reguler (wajib). Ini literal, bukan ekspresi nilai (lihat di bawah). Flag ditulis di dalam pattern seperti (?i). Maksimal 128 karakter; jika melebihi, penyimpanan ditolak
valueTeks tempat pattern diterapkan (ekspresi nilai, wajib). Jika nilai hasil resolve melebihi 10.240 karakter (10KiB), eksekusinya gagal
  • Hasil: Match berupa Boolean, Capture berupa array atau null. Pada array, indeks 0 adalah keseluruhan kecocokan dan mulai dari 1 adalah grup tangkapan; grup yang tidak berpartisipasi bernilai null (bukan string kosong. String kosong berarti cocok). Jika pattern tidak muncul, Capture bernilai null, bukan array kosong.
  • Kedua mode sama-sama menanyakan "apakah pattern muncul di suatu tempat". Jika seluruh teks harus sama dengan pattern, kunci dengan ^…$. Pertanyaannya dibuat sama agar dua statement, yaitu memeriksa dengan Match lalu mengambil dengan Capture, tidak memberi jawaban yang berbeda.
  • pattern adalah satu dari dua field pada engine ini yang bukan ekspresi nilai (yang satu lagi adalah key pada Cache). Jika pattern yang datang dari permintaan dijalankan apa adanya, pemanggil dapat memilih ekspresi yang akan dijalankan, dan backtracking ekspresi reguler menjadikan hal itu sarana penolakan layanan. Karena itu { /pointer } di dalam pattern pun tidak diubah menjadi nilai dan tetap menjadi bagian pattern secara harfiah.
  • Pattern dikompilasi satu kali untuk seluruh definisi ketika eksekusi dimulai. Meski berada di dalam Loop atau ResourceForEach, ia tidak dikompilasi ulang setiap iterasi, dan pattern yang tidak dapat dipakai akan gagal sebelum statement pertama melakukan apa pun (dapat ditangani dengan Try).
// Menguraikan "t=1492774577,v1=<hex 64 karakter>" menjadi { /sig/1 } = timestamp, { /sig/2 } = kode
{ "type": "Regex", "name": "sig", "mode": "Capture",
  "pattern": "^t=(\\d+),v1=([0-9a-f]{64})$", "value": "{ /headers/x-provider-signature }" }
 
// Hanya memeriksa format
{ "type": "Regex", "name": "isOrderId", "mode": "Match",
  "pattern": "^ORD-\\d{8}-\\d{4}$", "value": "{ /payload/orderId }" }

Alur kontrol

If

Percabangan kondisional. condition adalah JsonLogic, dan nilai benar/salah mengikuti aturan penilaian benar dan salah.

FieldDeskripsi
conditionJsonLogic (dievaluasi sebagai boolean)
thenArray Statement yang dijalankan saat benar
else(opsional) Array Statement yang dijalankan saat salah
{ "type": "If",
  "condition": { "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] },
  "then": [ { "type": "Return", "value": { "ok": false, "reason": "insufficient credit" } } ],
  "else": [ /* ... */ ] }

Loop

Perulangan. Pilih satu mode: over (foreach), while (kondisi), atau for (hitungan). Dalam mode apa pun, engine menerapkan batas atas iterasi (untuk mencegah perulangan tak terbatas). Batas atas itu dideklarasikan dengan maxIterations, dan jika tidak dituliskan, batas atas platform yang berlaku. Di dalam body juga dapat dimuat panggilan eksternal (Http·EmailSend) dan penyerapan file Media, dan statement panggilan eksternal benar-benar dipanggil pada tiap iterasi saat dijalankan. Batas jumlah panggilan eksternal maksimum per satu definisi tetap berlaku.

Pada anggaran waktu ia dihitung sebagai perkalian. Waktu yang dideklarasikan statement ini adalah waktu yang dideklarasikan body dikalikan maxIterations (atau 10.000 jika tidak ada) (Anggaran waktu). Jika body tidak memuat panggilan eksternal, waktu deklarasinya 0 sehingga anggaran dasar 30 detik menjadi batas yang sesungguhnya.

FieldDeskripsi
overforeach: ekspresi nilai yang di-resolve menjadi array
whilekondisi: JsonLogic (mengulang selama benar)
forhitungan: { "from", "to", "step"? }. Dari from hingga to inklusif; step default 1
maxIterationsJumlah iterasi maksimum (opsional). Jika tidak dituliskan, batas atas platform 10.000 yang berlaku, dan nilai yang lebih besar dari itu ditolak saat disimpan
name(opsional) Nama untuk mengikat item saat ini (foreach) atau indeks (while·for) ({ /<name> })
bodyArray Statement untuk body perulangan
// 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

Menjalankan cabang-cabang secara bersamaan dan melanjutkan setelah join. Referensi antar-cabang tidak diperbolehkan (jika ada ketergantungan, tempatkan secara berurutan).

FieldDeskripsi
branchesStatement[][]. Setiap elemen adalah satu cabang (array statement)
{ "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

Ini adalah return seperti dalam pemrograman pada umumnya. Mengembalikan hasil Script kepada pemanggil dan berhenti secara normal pada titik itu.

FieldDeskripsi
value(opsional) Ekspresi nilai yang akan dikembalikan
isErrorDefault false. Jika true, value dikembalikan sebagai error pada respons (jika tidak, sebagai return)
statusCodeKode status respons. Default 200
  • Jika Return tidak pernah tercapai, tidak ada nilai yang dikembalikan. Untuk mengembalikan hasil, tentukan value secara eksplisit.
  • Karena ini adalah penghentian normal, bukan pengecualian atau throw, ini bukan target catch (bahkan di dalam Try, ini menghentikan seluruh Script, tetapi finally tetap dijalankan).
  • Guard juga diekspresikan dengan statement ini. Jika Anda menempatkan Return pada then milik If, statement itu mengembalikan nilai ketika kondisi dilanggar dan tidak menjalankan statement setelahnya. Ini adalah salah satu dari beberapa kegunaan Return.
{ "type": "Return", "value": { "orderId": "{ /order/sys/id }", "status": "paid" }, "statusCode": 201 }
{ "type": "Return", "value": { "reason": "payment failed" }, "isError": true, "statusCode": 402 }

Try

Penanganan pengecualian.

FieldDeskripsi
bodyArray Statement yang akan dicoba
catch(opsional) Dijalankan saat body gagal. Mengekspos { message } di /error (statement mana yang gagal tidak disertakan)
finally(opsional) Selalu dijalankan terlepas dari berhasil atau gagal
  • Jika catch menanganinya, Script tidak dibatalkan. Hanya kegagalan tanpa catch yang membatalkan Script (termasuk upaya kompensasi).
  • Apa yang dianggap "kegagalan", dan batasan kompensasi (compensation), dibahas dalam Semantik eksekusi, batasan, dan keamanan.
{ "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": [ /* selalu dijalankan */ ] }