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
| Kategori | type | Ringkasan satu baris |
|---|---|---|
| Penulisan resource | ResourceCreate | Membuat Content/Media (opsional publikasi) |
ResourceUpdate | Penggantian penuh field Content/Media (field/locale yang tidak diberikan akan dihapus) | |
ResourcePatch | Penggabungan sebagian field Content/Media (hanya field/locale yang ditentukan; null literal menghapus) | |
ResourceDelete | Menghapus (hanya Draft/Archived; jika Published, batalkan publikasi dulu) | |
ResourcePublish / ResourceUnpublish | Publikasikan / batalkan publikasi | |
ResourceArchive / ResourceUnarchive | Arsipkan / batalkan pengarsipan | |
| Pembacaan resource | ResourceRead | Membaca satu item berdasarkan id |
ResourceFind | Satu item pertama yang cocok berdasarkan filter (null jika tidak ada) | |
ResourceForEach | Menelusuri resource yang cocok dengan filter secara internal dan menjalankan onEach untuk tiap item | |
ResourceCount | Hanya menghitung jumlah yang cocok dengan filter (item tidak dibaca) | |
| Eksternal | Http | Panggilan HTTP eksternal ({ status, body }) |
EmailSend | Mengirim 1 email melalui EmailAccount terdaftar | |
| Variabel | SetVar | Mendeklarasikan/memperbarui variabel berlingkup script |
| Cache | Cache | Membaca/menulis/menghapus cache berumur pendek milik Script itu sendiri |
| Parsing nilai | ParseJson | Mem-parsing teks JSON menjadi nilai (objek, array, skalar) lalu mengikatnya |
| Tanda tangan dan teks | Signature | Memverifikasi apakah kode tanda tangan yang diterima sama dengan kode yang dibuat memakai kunci rahasia (Boolean) |
Hash | Menghitung digest tanpa kunci (string) | |
Regex | Menerapkan ekspresi reguler. Kecocokan (Boolean) atau grup tangkapan (array) | |
| Alur kontrol | If | Percabangan kondisional |
Loop | Perulangan (foreach / while / counted) | |
Parallel | Menjalankan cabang secara bersamaan | |
Return | Mengembalikan hasil dan berhenti lebih awal | |
Try | Penanganan pengecualian (catch/finally) |
Statement Content yang tidak menunjuk targetnya dengan id wajib menuliskan Content Type yang ditanganinya. Pada
ResourceFind·ResourceForEach·ResourceCount,contentTypebersifat wajib bilaresourcebernilai"Content". Tidak ada kueri Content yang melintasi seluruh Space.ResourceCreatepun 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·ResourceDeleteserta statement publikasi dan pengarsipan) memilikitargetsehingga 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:
namemerupakan 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.idbiasanya berupa literal (mis."ct_post").target.sys.idbiasanya 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.
| Field | Berlaku untuk | Deskripsi |
|---|---|---|
resource | Umum | "Content" atau "Media" (wajib) |
contentType | Content | Content Type yang akan dibuat ({ sys: { id } }). Wajib untuk Content |
fields | Umum | Map 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) |
locale | Umum | (kemudahan) Jika diberikan, setiap nilai dalam fields otomatis dibungkus menjadi { <locale>: nilai } |
publish | Umum | Publikasikan setelah penulisan (tampil di CDA/ACDA). Default true |
filedariMedia: nilaifields.file.{locale}adalah instruksi ingest{ "source": <ekspresi nilai>, "encoding": "url"|"base64" }(keduanya wajib). Pada penulisan yang menyertakan file, engine melakukan penyerapan itu (jikaurl, ia mengunduh; jikabase64, 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). Jikapublish:truetetapi tidak ada file atau pemrosesan belum selesai, tahap publikasi menghasilkan error; jikapublish:false, statusnya tetapDraft.- 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.
| Field | Deskripsi |
|---|---|
resource | "Content" atau "Media" |
target | Target ({ sys: { id } }, wajib). id biasanya berupa { /ptr } |
fields | Seluruh 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) |
publish | Publikasikan 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.
| Field | Deskripsi |
|---|---|
resource | "Content" atau "Media" |
target | Target ({ sys: { id } }, wajib). id biasanya berupa { /ptr } |
fields | Field 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) |
publish | Publikasikan ulang setelah pembaruan. Default true |
- Menghapus locale atau file tertentu: berikan
nullliteral 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
fileMedia, 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).
| Field | Deskripsi |
|---|---|
resource | "Content" atau "Media" |
target | Target ({ 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.
| Field | Deskripsi |
|---|---|
resource | "Content" atau "Media" |
target | Target ({ 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·ResourceDeleteserta 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
settingspada SpaceRole penulis memuatSETTING_SERVICE_LOGIN(atauSETTING_ALL). Alasannya, direktori anggota adalah resource yang dikelola pengaturan Space di semua jalur lainnya juga. Jika tidak ada, penyimpanan ditolak (lihat Model keamanan). fromhanya menerimaCurrent. Anggota bukan resource yang dipublikasikan, sehingga memberiPublishedmembuat eksekusi gagal.contentTypedanadvanceddiabaikan. Direktori anggota tidak terbagi per Content Type (satu set untuk seluruh Space), dan Pencarian Lanjutan pun khusus Content.sys.emailpadawherehanya menerima operator keluarga kecocokan persis (eq·ne·in·nin). Alamat anggota disimpan terenkripsi, sehingga perbandingan urutan atauprefixtidak 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 berikansys.id-nya padatoServiceUsermilikEmailSend(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.
| Field | Deskripsi |
|---|---|
resource | "Content"·"Media"·"ServiceUser" |
target | Target ({ 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).
| Field | Deskripsi |
|---|---|
resource | "Content"·"Media"·"ServiceUser" |
contentType | Content Type yang menjadi cakupan pencarian ({ sys: { id } }). Wajib untuk Content. Diabaikan pada Media dan ServiceUser |
where | Filter ({ "<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) |
order | Urutan 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
namestatement ini. Referensikan secara langsung dengan{ /<name>/fields/<field>/<locale> }. Karena bernilainullsaat 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).
| Field | Deskripsi |
|---|---|
resource | "Content"·"Media"·"ServiceUser" (wajib) |
contentType | Content Type yang menjadi cakupan penelusuran ({ sys: { id } }). Wajib untuk Content. Diabaikan pada Media dan ServiceUser |
where | Filter ({ "<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" |
order | Urutan (mis. "sys.createdAt,sys.id"). Jika tidak ada, urutan bawaan platform |
from | Current (default, draft terbaru) atau Published (snapshot terpublikasi). ServiceUser hanya Current |
advanced | Telusuri 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 |
onEach | Array statement anak yang dijalankan untuk tiap item (wajib) |
- Tidak mengikat koleksi (
foreach, bukanmap). Tidak ada{ items, next }maupun cursor. Alih-alih mengembalikan hasil penelusuran sebagai nilai, ia menjalankanonEachuntuk tiap item. Jika Anda memerlukan daftarnya, kumpulkan sendiri denganSetVar. Jika yang Anda perlukan hanya jumlahnya, gunakanResourceCount. - Tanpa
limitpun 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, mencapailimityang Anda deklarasikan adalah penghentian yang disengaja sehingga berakhir normal.limityang 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
onEachyang tidak tertangani), berarti gagal, dan error menunjuk pada item mana serta mengapa gagal. Pelanjutan diekspresikan penulis dengan datanya sendiri (jadikanwherebermakna "belum diproses" dan tandai penyelesaian di akhironEach, sehingga eksekusi ulang melanjutkan dari sisa yang ada). - Pada anggaran waktu ia dihitung sebagai perkalian. Waktu yang dideklarasikan statement ini adalah waktu yang dideklarasikan
onEachdikalikan 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 dalamonEach-lah yang dihitung ke anggaran. onEachdapat memuat panggilan eksternal (Http·EmailSend) atau penyerapan file Media seperti statement lainnya (sama sepertibodypadaLoop). 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.
| Field | Deskripsi |
|---|---|
resource | "Content" atau "ContentType" (wajib). Media dan ServiceUser tidak dapat dihitung, dan menuliskannya seperti itu membuat penyimpanan ditolak |
contentType | Content Type yang menjadi cakupan penghitungan ({ sys: { id } }). Wajib untuk Content. Diabaikan ketika yang dihitung adalah Content Type (seluruh Space adalah satu set) |
where | Filter. 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) atauResourceForEach(dijalankan untuk tiap item). - Jangan menghitung dengan menelusuri
ResourceForEachhanya 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
orderdanlimit. 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).
| Field | Deskripsi |
|---|---|
method | "GET", "POST", "PUT", "PATCH", "DELETE" |
url | URL 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) |
body | Body permintaan (ekspresi nilai atau JSON). Format pengirimannya ditentukan oleh header Content-Type |
timeoutMs | Batas waktu untuk panggilan ini (ms) |
retry | Jumlah percobaan ulang ketika status respons 400 atau lebih. Default 0; batas atasnya 2 |
ignoreStatusCode | Apakah 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) |
responseType | Bentuk penerimaan isi respons. "Json" (default) mem-parsingnya menjadi objek atau array, "Text" menerimanya sebagai string |
- Hasil:
{ status, body }. Jika statement ini diberiname,{ /<name>/status },{ /<name>/body/... }. Bentukbodyditentukan olehresponseType. responseTypehanya 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 targetTry/catch). Untuk API yang tidak mengembalikan JSON, terima responsnya sebagai"Text", lalu parsing denganParseJsonbila perlu diperlakukan sebagai nilai. "Text"didekode dengan charset dariContent-Typerespons, dan dianggap UTF-8 bila tidak ada charset. Bila isinya kosong,bodybernilainullpada 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 olehignoreStatusCode).
{ "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 dideklarasikan | Bentuk body | Body yang dikirim |
|---|---|---|
application/json | Apa pun | JSON |
application/x-www-form-urlencoded | Objek atau array | order[id]=A-2481&order[amount]=34000 |
text/plain | Skalar | Nilai apa adanya |
Lainnya (text/xml dll.) | Apa pun | JSON |
Berikut kombinasi yang dikoreksi karena tidak dapat dimuat dalam format yang dideklarasikan.
Content-Type yang dideklarasikan | Bentuk body | Content-Type yang benar-benar dikirim | Body yang dikirim |
|---|---|---|---|
application/x-www-form-urlencoded | Skalar | text/plain;charset=UTF-8 | Nilai apa adanya |
text/plain | Objek atau array | application/json | JSON |
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.
body | Key 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.
| Field | Deskripsi |
|---|---|
account | Referensi 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 |
to | Alamat penerima (ekspresi nilai). Gunakan tepat salah satu dari to atau toServiceUser |
toServiceUser | Menentukan 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 |
cc | Array alamat penerima tembusan (ekspresi nilai) |
bcc | Array alamat penerima tersembunyi (ekspresi nilai) |
subject | Subjek (ekspresi nilai, wajib) |
body | Body (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, danbcc(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 denganResourceForEach+EmailSend. - Tidak mengikat hasil. Keberhasilan hanya berarti "penyedia menerima email", sehingga tidak ada nilai yang dikembalikan dan
nametidak diterima. Juga tidak melakukan percobaan ulang (email tidak idempoten, sehingga percobaan ulang setelah kegagalan yang ambigu akan menyebabkan pengiriman ganda. Karena itu ia tidak mengikutiretrymilikHttp). Kegagalan di-throw dan ditangani olehcatchpadaTry. - 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 padaHttp). Dapat dipakai di dalamonEachmilikResourceForEach(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).
| Field | Deskripsi |
|---|---|
var | Nama variabel. Direferensikan dengan { /vars/<var> } |
value | Ekspresi 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 arrayCache
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.
| Field | Deskripsi |
|---|---|
action | Salah satu dari "Set" (menulis), "Get" (membaca), atau "Delete" (menghapus) (wajib) |
key | Kunci cache (Cache Key) (wajib). Ini literal, bukan ekspresi nilai (lihat di bawah). Maksimal 128 karakter; jika melebihi, penyimpanan ditolak |
value | Nilai yang akan disimpan (khusus Set) |
ttl | Lama cache bertahan (khusus Set, dalam detik). Antara 1 dan 30; jika dihilangkan, nilainya 5 |
defaultValue | Nilai yang diikat Get ketika tidak ada data yang tersimpan di cache (khusus Get). Jika dihilangkan, nilainya null |
name | Nama 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
ttlpadaGetataudefaultValuepadaSetmembuat penyimpanan ditolak. - Data yang tidak ada dan data yang sudah kedaluwarsa tidak dibedakan. Keduanya mengikat
defaultValue. Begitu pula ketika yang tersimpan adalahnull. - 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.
keybersifat 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, jikakeymemuat{ /pointer }, ia tidak diubah menjadi nilai dan tidak juga dipakai secara harfiah. Penyimpanannya sendirilah yang ditolak.- Tidak boleh diletakkan di dalam perulangan. Jika ada
Cachedi dalam blokLoopatauResourceForEach, 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.
| Field | Deskripsi |
|---|---|
name | Nama 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 |
value | Teks 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
42atau"a"juga di-parsing. Setelah itu tunjuk bagian dalamnya dengan{ /<name>/... }. - Nilai yang sudah di-parsing diikat apa adanya. Ketika
valuemenghasilkan 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.nullmencakup dua kasus berbeda. Jika teks yang di-parsing hanya satu katanull, itu normal dan hasilnya juganull. Sebaliknya, jika tempat yang ditunjukvaluekosong sehingga tidak ada nilai sama sekali, tidak ada yang bisa di-parsing dan statement gagal.- Gagal: ketika
valuemenghasilkan tanpa nilai atau hanya spasi, dan ketika teksnya bukan JSON. Tangani denganTry/catchseperti 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.
| Field | Deskripsi |
|---|---|
name | Nama yang menampung hasil verifikasi (wajib). { /<name> } bernilai true atau false. Memverifikasi tetapi tidak memakai hasilnya sama saja dengan tidak memverifikasi, sehingga tidak boleh dihilangkan |
algorithm | Hash yang dipakai untuk membuat kode (wajib). SHA1·SHA256·SHA384·SHA512 |
secret | Kunci rahasia yang Anda bagi dengan pihak lain (ekspresi nilai, wajib) |
secretEncoding | Notasi 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 |
value | Pesan 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 |
expected | Kode yang dikirim pemanggil (ekspresi nilai, wajib). Mis. { /headers/x-signature } |
- Hasil:
Boolean. Setelah itu pakai{ /<name> }apa adanya sebagai kondisiIf. valueditulis dengan/rawPayload, bukan/payloadyang 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
algorithmmengunci 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
falsedibedakan oleh siapa yang memberi nilai itu.- Jika
expectedtidak ada atau kodenya tidak cocok, hasilnya hanyafalse, bukan kegagalan. Alasannya, memberi tahu ketiadaan header dan ketidakcocokan kode secara terpisah berarti mengajari pengirim bagian mana yang salah. - Jika
valuekosong, kode dihitung dengan pesan kosong. Body kosong pun adalah objek tanda tangan. - Jika
secrettidak ada atau bukan notasi yang dideklarasikansecretEncoding, itu kegagalan. Di antara ketiganya, hanya inilah masukan penulis sendiri.secretdanvaluetidak disertakan dalam pesan kegagalan.
- Jika
- Batas atas
valueadalah 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.
secrettidak disimpan terenkripsi. Berbeda dengansecret: truepada headerHttp(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".
| Field | Deskripsi |
|---|---|
name | Nama yang menampung digest (wajib) |
algorithm | MD5·SHA1·SHA256·SHA384·SHA512 (wajib). MD5 ada untuk mereproduksi skema lama yang mengharuskannya, bukan nilai yang perlu Anda pilih untuk tanda tangan baru |
value | Pesan yang akan di-digest (ekspresi nilai, wajib) |
encoding | Notasi 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 dalamvaluejustru 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 milikSignature. - Jika
valuedi-resolve tanpa nilai atau hanya berisi spasi, itu kegagalan (karena ini ekspresi penulis sendiri). - Batas atas
valueadalah 128 karakter. Karena tempat ini memuat beberapa field yang disambung, batasnya jauh lebih sempit daripadaSignature. Jika Anda harus menghitung atas seluruh body webhook, pakaiSignature.
// 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=….
| Field | Deskripsi |
|---|---|
name | Nama yang menampung hasil (wajib). Pada Capture, elemennya ditunjuk dengan { /<name>/1 } |
mode | "Match" mengikat kecocokan sebagai Boolean, "Capture" mengikat kecocokan pertama sebagai array (wajib) |
pattern | Ekspresi reguler (wajib). Ini literal, bukan ekspresi nilai (lihat di bawah). Flag ditulis di dalam pattern seperti (?i). Maksimal 128 karakter; jika melebihi, penyimpanan ditolak |
value | Teks tempat pattern diterapkan (ekspresi nilai, wajib). Jika nilai hasil resolve melebihi 10.240 karakter (10KiB), eksekusinya gagal |
- Hasil:
MatchberupaBoolean,Captureberupa array ataunull. Pada array, indeks0adalah keseluruhan kecocokan dan mulai dari1adalah grup tangkapan; grup yang tidak berpartisipasi bernilainull(bukan string kosong. String kosong berarti cocok). Jika pattern tidak muncul,Capturebernilainull, 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 denganMatchlalu mengambil denganCapture, tidak memberi jawaban yang berbeda. patternadalah satu dari dua field pada engine ini yang bukan ekspresi nilai (yang satu lagi adalahkeypadaCache). 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
LoopatauResourceForEach, ia tidak dikompilasi ulang setiap iterasi, dan pattern yang tidak dapat dipakai akan gagal sebelum statement pertama melakukan apa pun (dapat ditangani denganTry).
// 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.
| Field | Deskripsi |
|---|---|
condition | JsonLogic (dievaluasi sebagai boolean) |
then | Array 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.
| Field | Deskripsi |
|---|---|
over | foreach: ekspresi nilai yang di-resolve menjadi array |
while | kondisi: JsonLogic (mengulang selama benar) |
for | hitungan: { "from", "to", "step"? }. Dari from hingga to inklusif; step default 1 |
maxIterations | Jumlah 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> }) |
body | Array 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).
| Field | Deskripsi |
|---|---|
branches | Statement[][]. 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.
| Field | Deskripsi |
|---|---|
value | (opsional) Ekspresi nilai yang akan dikembalikan |
isError | Default false. Jika true, value dikembalikan sebagai error pada respons (jika tidak, sebagai return) |
statusCode | Kode status respons. Default 200 |
- Jika
Returntidak pernah tercapai, tidak ada nilai yang dikembalikan. Untuk mengembalikan hasil, tentukanvaluesecara eksplisit. - Karena ini adalah penghentian normal, bukan pengecualian atau throw, ini bukan target
catch(bahkan di dalamTry, ini menghentikan seluruh Script, tetapifinallytetap dijalankan). - Guard juga diekspresikan dengan statement ini. Jika Anda menempatkan
ReturnpadathenmilikIf, statement itu mengembalikan nilai ketika kondisi dilanggar dan tidak menjalankan statement setelahnya. Ini adalah salah satu dari beberapa kegunaanReturn.
{ "type": "Return", "value": { "orderId": "{ /order/sys/id }", "status": "paid" }, "statusCode": 201 }
{ "type": "Return", "value": { "reason": "payment failed" }, "isError": true, "statusCode": 402 }Try
Penanganan pengecualian.
| Field | Deskripsi |
|---|---|
body | Array 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
catchmenanganinya, Script tidak dibatalkan. Hanya kegagalan tanpacatchyang 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 */ ] }Dokumen terkait
- Ekspresi nilai: aturan nilai yang diikuti oleh semua field di atas.
- Semantik eksekusi, batasan, dan keamanan: urutan eksekusi, error, batasan statis, dan keamanan.
- Cookbook: contoh lengkap yang mengombinasikan statement-statement ini.
- Ikhtisar Script: struktur tingkat atas dan waktu yang diberikan untuk satu eksekusi.
