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 tiga, yaitu pattern pada Regex, key pada Cache, dan color pada PdfWatermark, lihat Regex, Cache, dan Transformasi file.

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 }). Dapat mengirim file sebagai 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 yang menangani Content tanpa menunjuk targetnya dengan id wajib menuliskan Content Type. 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, secrets), 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.

Statement yang menerima ServiceUser (anggota yang mendaftar ke produk) adalah tiga Statement pembacaan (ResourceRead, ResourceFind, ResourceForEach) serta ResourceUpdate dan ResourcePatch. Field yang dapat ditulis oleh kedua Statement itu hanya dua, yaitu roleOverride dan enableLogin. Jika Anda menuliskannya pada Statement penulisan selain itu, penyimpanan ditolak (lihat Error). Aturannya dibahas di Menangani direktori anggota.

Penulisan resource

Setiap Statement penulisan memiliki propagateEvents (default false) (ResourceUpdate dan ResourcePatch yang menulis ServiceUser tidak menerima field ini). 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", "transformations": [ … ] }. source dan encoding wajib, sedangkan transformations hanya ditulis jika file yang diterima akan diubah sebelum disimpan (Transformasi file). 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" }

Transformasi file (transformations)

Saat menjual e-book, ada kalanya Anda ingin menyerahkan salinan yang setiap halaman PDF-nya dibubuhi email pembeli. Jika Anda menulis transformations pada instruksi ingest, file yang diterima dapat diubah sebelum disimpan. Transformasi yang didukung hanya satu, yaitu PdfWatermark.

Field instruksi ingestWajibDeskripsi
transformationsOpsionalArray objek transformasi. Jika dihilangkan atau berupa array kosong, file yang diterima dimasukkan ke Media tanpa diubah. Jika bukan array, penyimpanan Script ditolak
  • Diterapkan sesuai urutan array. Hasil transformasi sebelumnya menjadi input transformasi berikutnya, dan hasil terakhir menjadi file Media.
  • Dapat ditulis pada instruksi ingest mana pun di ResourceCreate, ResourceUpdate, ResourcePatch.
  • File asli yang ditunjuk source tidak berubah. Untuk membubuhkan teks pada file yang sudah ada di Space, baca Media itu dengan ResourceRead, lalu masukkan { /<name>/fields/file/<locale>/url } ke source untuk membuat Media baru.
  • Transformasi juga merupakan bagian dari ingest. Waktu yang dibutuhkan diambil dari anggaran dasar 30 detik (Anggaran waktu).

PdfWatermark: membubuhkan teks pada setiap halaman PDF

FieldWajibDeskripsi
typeWajib"PdfWatermark". Jika nilainya lain, penyimpanan Script ditolak
textWajibTeks yang akan dibubuhkan. Berupa string dan dapat dicampur dengan referensi { /pointer }. Objek operasi JsonLogic tidak diterima. Harus berupa satu baris, 1~64 karakter yang terdiri dari karakter yang dapat dipakai
colorOpsionalWarna teks. Ditulis sebagai #RRGGBB (enam digit heksadesimal), dan jika dihilangkan nilainya #808080 (abu-abu). Hanya menerima literal, bukan ekspresi nilai, dan jika formatnya berbeda, penyimpanan Script ditolak
  • Field yang tidak ada di tabel diabaikan. Ukuran huruf, posisi, dan transparansi tidak dapat ditentukan.
  • Dibubuhkan pada setiap halaman. Satu baris teks diletakkan di sepanjang diagonal yang melewati tengah halaman (dari kiri bawah ke kanan atas).
  • Dibubuhkan di atas isi halaman dengan opasitas 30%. Isi asli di bawah teks tetap terbaca.
  • Ukuran huruf ditentukan otomatis untuk setiap halaman. Teks dibubuhkan dengan ukuran yang menempati 80% panjang diagonal, tetapi tidak melebihi 15% sisi pendek halaman. Teks pendek dibubuhkan besar, teks panjang dibubuhkan kecil, dan teks tidak keluar dari halaman.
  • Fontnya adalah Helvetica, font standar PDF.
  • Apakah file berupa PDF ditentukan dari isinya, bukan dari nama file atau ekstensinya.
// bagian dari array statements. Media baru dari PDF e-book yang dibeli, dibubuhi email pembeli dan nomor pesanan
{ "type": "ResourceRead", "resource": "Media",
  "target": { "sys": { "id": "{ /payload/fields/ebookMediaId }" } }, "name": "ebook" },
{ "type": "ResourceCreate", "resource": "Media", "locale": "en-US", "publish": false,
  "fields": {
    "title": "{ /ebook/fields/title/en-US }",
    "file": {
      "source": "{ /ebook/fields/file/en-US/url }", "encoding": "url",
      "transformations": [
        { "type": "PdfWatermark", "text": "{ /payload/fields/email } · { /payload/fields/orderId }", "color": "#B22222" }
      ]
    }
  }, "name": "stamped" }

Salinan ini ditujukan untuk satu pembeli, sehingga publish diatur ke false agar tidak dikirimkan melalui CDA/ACDA. Jika dihilangkan, salinan dipublikasikan sesuai nilai default true.

Batasan teks PdfWatermark

text hanya dapat memuat karakter yang dapat digambar oleh font standar PDF (set karakter WinAnsi).

JenisKarakter
Karakter cetak ASCIISpasi, huruf Latin besar dan kecil, angka, serta simbol dasar seperti @ . # { }
Karakter Latin-1Huruf dan simbol Eropa Barat seperti é ü ñ ß · © °. Spasi tak terputus (U+00A0) dan tanda hubung lunak (U+00AD) dikecualikan
Simbol tambahan€ ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • ˜ ™ š › œ ž Ÿ, serta dua jenis tanda pisah (U+2013, U+2014)

Karakter selain itu tidak dapat dipakai. Yang termasuk di sini adalah karakter Latin Extended yang tidak ada di tabel (seperti ł, ő, ș), karakter non-Latin, emoji, serta baris baru dan tab.

Saat Script disimpan, text diperiksa sebagaimana ditulis. Nilai hasil resolve dari text yang memuat { /pointer } baru diketahui saat eksekusi, sehingga nilai hasil resolve itu diperiksa lagi saat eksekusi. Kode error yang terjadi saat penyimpanan ada di Error.

Batasantext literaltext yang memuat { /pointer }
Satu baris yang terdiri dari karakter yang dapat dipakaiPenyimpanan Script ditolakSeluruh string tertulis (termasuk path pointer) diperiksa saat penyimpanan, dan penyimpanan Script ditolak. Nilai hasil resolve diperiksa lagi saat eksekusi dan gagal dengan status 400
1~64 karakterPenyimpanan Script ditolakTidak diperiksa saat penyimpanan. Saat eksekusi, panjang nilai hasil resolve diperiksa dan gagal dengan status 400
  • Hitung panjang teks tetap saat menulis. Pada "Purchased by { /payload/fields/email } · Order { /payload/fields/orderId }", teks tetap di luar template berjumlah 22 karakter, sehingga panjang yang tersisa untuk email dan nomor pesanan adalah 42 karakter secara gabungan. Jika nilai sebenarnya yang masuk lebih panjang dari itu, Script tetap tersimpan tetapi eksekusi tersebut gagal.
  • Bubuhkan pengenal, bukan nama. Nama pembeli yang memuat karakter yang tidak ada di tabel akan gagal saat eksekusi. Email atau nomor pesanan yang berupa ASCII dapat dibubuhkan.

Kasus ketika transformasi file gagal

Dalam kasus berikut, Statement tersebut gagal. Seperti kegagalan runtime lainnya, kegagalan ini dapat ditangani dengan Try/catch. Transformasi terjadi sebelum Media ditulis, sehingga jika gagal, Media baru tidak dibuat dan target ResourceUpdate, ResourcePatch juga tidak berubah.

SituasiHasil
File pada source bukan PDFGagal dengan status 422
PDF tidak dapat dibaca, atau terenkripsi (termasuk PDF yang tidak meminta kata sandi saat dibuka tetapi hanya membatasi izin)Gagal dengan status 422
text hasil resolve kosong atau hanya berisi spasiGagal dengan status 400
text hasil resolve berada di luar rentang 1~64 karakter atau memuat karakter yang tidak dapat dipakaiGagal dengan status 400

ResourceUpdate

Mengganti sepenuhnya field dari Content, Media, atau ServiceUser 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", "Media", "ServiceUser". Field yang dapat ditulis pada ServiceUser beserta batasannya ada di Menangani direktori anggota
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). ServiceUser tidak menerimanya
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, Media, atau ServiceUser 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", "Media", "ServiceUser". Field yang dapat ditulis pada ServiceUser beserta batasannya ada di Menangani direktori anggota
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). ServiceUser tidak menerimanya
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 Menangani 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.

Menangani 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 kelima Statement yang menangani direktori anggota.

  • Field yang dapat diubah hanya dua. ResourceUpdate dan ResourcePatch menerima "ServiceUser" lalu mengubah roleOverride dan enableLogin. Jika pada ResourcePatch Anda memberi null untuk roleOverride, peran yang sudah ditetapkan akan dilepas. ResourceCreate, 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, melainkan karena Script tidak dapat membuat atau menghapus anggota, sehingga penolakannya bukan error izin melainkan Statement yang salah tulis.
  • Anggota tidak memiliki version maupun propagateEvents. Jika Anda menuliskan salah satunya, eksekusinya gagal. Karena ResourceUpdate mengganti seluruh field yang dapat ditulis, enableLogin bersifat wajib, dan jika roleOverride tidak dituliskan, peran yang sudah ditetapkan akan dilepas. Untuk mengubah salah satunya saja, gunakan ResourcePatch, dan pada kasus itu setidaknya satu dari kedua field tersebut harus dituliskan.
  • 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 (Menangani 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. Pada Anggaran waktu, timeoutMs ditambahkan, dan jika Anda mengirim file, waktu unduh untuk setiap file juga ditambahkan. Jika retry ditentukan, jumlah itu dikalikan dengan jumlah percobaan (1 + retry).

FieldDeskripsi
method"GET", "POST", "PUT", "PATCH", "DELETE"
urlURL target (ekspresi nilai; { /ptr } dapat disisipkan)
headers[{ "key", "value" }]. value berupa ekspresi nilai. Nilai rahasia seperti API key tidak ditulis apa adanya, melainkan dideklarasikan di secrets lalu dibaca sebagai { /secrets/<key> } (contoh: "Bearer { /secrets/LLM_API_KEY }"). 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
urlBodyURL file yang akan dikirim sebagai body permintaan (ekspresi nilai). Platform mengambil file dari URL ini lalu mengirim byte-nya apa adanya (Mengirim file sebagai body)
multipartBodyArray part yang dikirim sebagai multipart/form-data (1 atau lebih). Part teks dan part file dapat dicampur (Mengirim file sebagai body)
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
  • Gunakan hanya salah satu dari body, urlBody, dan multipartBody. Jika dua atau lebih dituliskan, penyimpanan ditolak. Jika ketiganya tidak ada, permintaan dikirim tanpa body.
  • 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 { /secrets/LLM_API_KEY }" } ],
  "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" }

Mengirim file sebagai body

Ada kalanya file yang telah diunggah ke Media perlu diteruskan ke API eksternal. Byte file tidak dapat dimuat dalam ekspresi nilai. Sebagai gantinya, jika Anda menentukan URL tempat file berada, platform mengambil file dari URL itu lalu mengirimkannya sebagai body permintaan. Untuk mengirim satu file sebagai seluruh body, gunakan urlBody; untuk mengirim file bersama field teks sebagai formulir, gunakan multipartBody.

urlBody: mengirim satu file sebagai seluruh body

File yang diambil dari URL yang ditentukan dikirim apa adanya sebagai body permintaan tanpa diubah. Cara ini cocok untuk API yang menerima unggahan file dengan PUT ke URL unggahan. Berikut contoh membaca PDF kontrak (Media) lalu mengirimkannya ke URL unggahan layanan tanda tangan elektronik.

// bagian dari array statements
{ "type": "ResourceRead", "resource": "Media",
  "target": { "sys": { "id": "{ /payload/fields/contractMediaId }" } }, "name": "contract" },
{ "type": "Http", "method": "PUT",
  "url": "https://api.esign.example/v1/envelopes/{ /payload/fields/envelopeId }/document",
  "headers": [ { "key": "Authorization", "value": "Bearer { /secrets/ESIGN_API_KEY }" },
               { "key": "Content-Type", "value": "application/pdf" } ],
  "urlBody": "{ /contract/fields/file/en-US/url }", "name": "upload" }

Content-Type permintaan ditentukan dengan urutan berikut.

  1. Jika Content-Type ditentukan pada headers, nilai itu dipakai apa adanya. Koreksi pada serialisasi body tidak diterapkan.
  2. Jika tidak ditentukan atau nilainya kosong, Content-Type yang dikembalikan URL file dipakai.
  3. Jika itu pun tidak ada, application/octet-stream dipakai.

multipartBody: mengirim file dan teks sebagai formulir

Permintaan dikirim sebagai multipart/form-data. Satu elemen array menjadi satu part, dan part dikirim sesuai urutan array. Array harus berisi 1 elemen atau lebih, dan array kosong membuat penyimpanan ditolak. Content-Type selalu multipart/form-data (string pembatas dibuat otomatis), dan Content-Type yang ditulis pada headers diabaikan.

Field partDeskripsi
nameNama field formulir (wajib). Ini literal, bukan ekspresi nilai. Jika nama yang sama dipakai beberapa kali, masing-masing dikirim sebagai part terpisah
valueIsi part teks (ekspresi nilai). Jika hasilnya kosong, dikirim sebagai nilai kosong
urlURL tempat byte part file diambil (ekspresi nilai). Mis. url milik file Media
fileNameNama file yang dimuat pada Content-Disposition part (ekspresi nilai). Hanya dapat dipakai pada part url
contentTypeContent-Type part (ekspresi nilai)
  • Tuliskan tepat salah satu dari value dan url. Jika keduanya ditulis atau keduanya tidak ada, penyimpanan ditolak. Menulis fileName pada part value juga membuat penyimpanan ditolak.
  • Jika fileName dihilangkan, potongan path terakhir dari url didekode lalu dipakai (query string tidak termasuk). Jika potongan itu kosong atau memuat karakter kontrol, nama yang dikirim adalah file.
  • Jika contentType dihilangkan, part value dikirim tanpa Content-Type, dan part url memakai Content-Type yang dikembalikan URL file (jika tidak ada, application/octet-stream). Jika Anda menentukan nilai yang bukan format media type, Http itu gagal dengan status 400 saat dijalankan.
  • Jika hasil ekspresi nilai fileName dan contentType memuat karakter baris baru, Http itu gagal dengan status 400 sebelum permintaan dikirim.

Berikut contoh mengirim foto produk (Media) bersama opsi analisis ke layanan analisis gambar. Part image tidak menuliskan fileName, sehingga potongan terakhir URL file Media (mis. tumbler.png) dikirim sebagai nama file.

// bagian dari array statements
{ "type": "ResourceRead", "resource": "Media",
  "target": { "sys": { "id": "{ /payload/fields/photoMediaId }" } }, "name": "photo" },
{ "type": "Http", "method": "POST", "url": "https://api.vision.example/v2/analyze",
  "headers": [ { "key": "x-api-key", "value": "{ /secrets/VISION_API_KEY }" } ],
  "multipartBody": [
    { "name": "image", "url": "{ /photo/fields/file/en-US/url }" },
    { "name": "language", "value": "ko" },
    { "name": "features", "value": "labels" },
    { "name": "features", "value": "text" }
  ], "timeoutMs": 20000, "name": "analysis" }

Kegagalan yang terjadi pada Http yang mengirim file juga terbagi menjadi dua jenis. Batas waktu yang terlampaui diperlakukan sebagai respons status 504, sehingga retry dan ignoreStatusCode berlaku. Pada kegagalan terkait file dalam tabel di bawah, keduanya tidak berlaku. Klasifikasi seluruh kegagalan Http dirangkum di Dua cara Http gagal.

Jika ignoreStatusCode: true, { "status": 504, "body": "timed out after …ms" } diikat sebagai hasil ketika batas waktu terlampaui.

Satu permintaan harus selesai dalam timeoutMs, dan waktu menunggu respons URL file juga termasuk di dalamnya. Saat mengirim file, batas waktu diperpanjang sebanyak waktu yang dibutuhkan untuk mengirim file itu pada kecepatan 10.000.000 byte per detik. Waktu tambahan itu tidak melebihi waktu unduh paket dikalikan jumlah file. Namun jika URL file tidak memberi tahu ukuran file, perpanjangannya hanya sebanyak data yang benar-benar terkirim, sehingga jika transfer lambat atau berhenti di tengah, batas waktu dapat terlampaui lebih awal.

Dalam kasus berikut, Http itu langsung gagal. Kegagalan ini tidak dicoba ulang dengan retry, dan tetap diperlakukan sebagai kegagalan meskipun ignoreStatusCode: true ditentukan. Seperti kegagalan runtime lainnya, kegagalan ini dapat ditangani dengan Try/catch.

SituasiHasil
File yang akan diambil lebih besar daripada batas unduhan paketGagal dengan status 413. Jika URL file memberi tahu ukurannya lebih dulu, kegagalan terjadi sebelum file diambil; jika tidak, kegagalan terjadi saat batas terlampaui di tengah pengambilan
URL file merespons dengan status selain 2xx (termasuk pengalihan), atau URL file tidak dapat dihubungiGagal dengan status 502
contentType part bukan format media typeGagal dengan status 400
Nilai fileName, contentType part memuat karakter baris baruGagal dengan status 400. Kegagalan terjadi sebelum permintaan dikirim
  • Batas unduhan berlaku per URL file. Jika multipartBody memuat beberapa part file, batasnya berlaku terpisah untuk setiap file.
  • Byte yang dikirim, termasuk file, dijumlahkan ke "Panggilan eksternal" pada transfer data di Paket Harga.

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). 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 kecuali autoEscape bernilai false. Gunakan tepat salah satu dari body atau templateBody
templateBodyMembuat body dari sebuah template. Bentuknya { source, data } dan dipakai tepat salah satu dengan body. Lihat Membuat body dari template di bawah
autoEscape(opsional, bawaan true) Menentukan apakah nilai yang diinterpolasi ke body di-escape HTML. Sasarannya adalah hasil { /pointer } pada body dan nilai templateBody.data. Jika disetel false, markup yang ada di dalam nilai dirender apa adanya. Markup yang ditulis sendiri oleh penulis dan template templateBody.source memang bukan sasaran, dan field header pun tidak terpengaruh
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
  • Body ditulis dengan salah satu dari body atau templateBody. Jika keduanya ditulis bersamaan atau keduanya dikosongkan, penyimpanan ditolak.
  • Nilai yang dikirim pemanggil secara bawaan dirender sebagai huruf. Jika autoEscape aktif, markup seperti <a> di dalam nilai terlihat sebagai huruf, bukan sebagai tag. Ini mekanisme agar nilai yang dikirimkan orang lain tidak dapat menanam tautan atau tag ke dalam email orang lain. Setel false hanya ketika markup memang harus dipertahankan, seperti pada body newsletter yang ditulis sendiri oleh pengelola.
  • Field header tidak boleh memuat nilai yang mengandung baris baru. Ini berlaku untuk subject, replyTo dan semua alamat. Jika baris baru masuk, eksekusi gagal. Pada body, baris baru adalah isi yang wajar sehingga tidak diperiksa.
  • 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" }

Membuat body dari template (templateBody)

Jika body email ditulis langsung di dalam definisi Script, setiap kali kalimatnya diperbaiki Script harus ikut diubah. templateBody menempatkan template body di dalam Content dan hanya mengisi bagian kosongnya, sehingga kalimat diperbaiki di Content Studio dan Script tetap apa adanya.

KunciDeskripsi
sourceSatu pointer yang menunjuk tempat template berada (wajib). Ia harus menunjuk Content yang dibaca ResourceRead atau ResourceFind pada Script ini, dan tidak boleh ada huruf lain sebelum maupun sesudah pointer itu. Nilainya harus berupa string, jadi untuk Field multibahasa tulis sampai lokalnya (/tpl/fields/html/en-US)
dataNilai yang mengisi bagian kosong template (opsional). Nama kunci sekaligus menjadi nama bagian kosong. Karena ini tempat data, untuk memakai operasi bubuhkan $ pada operator ($cat), dan bagian kosong yang namanya diawali $ ditulis dengan $$ (Tempat data dan tempat ekspresi)

Bagian kosong di dalam template ditulis { /<kunci> }. Yang dibaca bagian kosong ini hanya data; template tidak dapat membaca /payload atau /secrets secara langsung. Bagian kosong yang menunjuk kunci yang tidak ada di data dirender sebagai huruf kosong. Kunci yang nilainya null pun sama. Ini kebalikan dari pointer pada body yang menunjuk null lalu tercetak sebagai huruf null, jadi perhatikan hal ini saat berpindah antara kedua cara tersebut.

Jika di dalam template ada tempat yang {-nya diikuti / (sekalipun ada spasi di antaranya, misalnya a{/*komentar*/} pada CSS), ia terbaca sebagai bagian kosong sehingga ditulis \{.

Ada tiga tempat yang lolos saat disimpan tetapi terputus saat dijalankan. Jika source ter-resolve menjadi objek (ketika lokal pada Field multibahasa terlewat), ia gagal. Jika source bernilai kosong, ia gagal (karena ResourceFind tidak menemukannya, atau karena Statement yang membaca template berada setelah EmailSend). Jika hasil pengisian seluruh bagian kosong hanya berupa spasi, ia juga gagal.

Template yang ditunjuk source sendiri tidak di-escape. Yang menjadi sasaran escape adalah nilai data. Karena itu, untuk memasukkan HTML kiriman pemanggil ke dalam email apa adanya, letakkan nilai tersebut di data dan setel autoEscape menjadi false.

{ "type": "ResourceRead", "resource": "Content", "name": "tpl",
  "target": { "sys": { "id": "cnt_mail_shipping" } } },
{ "type": "EmailSend", "account": { "sys": { "id": "eml_orders" } },
  "toServiceUser": { "sys": { "id": "{ /order/fields/buyer/en-US/sys/id }" } },
  "subject": "Pengiriman telah dimulai",
  "templateBody": {
    "source": "{ /tpl/fields/html/en-US }",
    "data": { "name": "{ /order/fields/buyerName/en-US }",
              "tracking": "{ /order/fields/trackingNo/en-US }" } } }

Templatenya (Field html pada cnt_mail_shipping) berbentuk seperti ini.

<p>Halo { /name }, pengiriman produk yang Anda pesan telah dimulai.</p>
<p>Nomor resi: { /tracking }</p>

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). Kunci dideklarasikan di secrets lalu ditulis sebagai { /secrets/<key> }
secretEncodingNotasi string kunci yang dirujuk oleh 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.
  • Kunci tanda tangan tidak ditulis apa adanya di secret. Deklarasikan kunci itu di secrets pada definisi, lalu tulis sebagai "{ /secrets/<key> }". Nilai di secrets dienkripsi dan disimpan dengan aman oleh WEEGLOO. secretEncoding menyatakan notasi string kunci yang disimpan di secrets.
// Penyedia yang menandatangani seluruh body
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
  "secret": "{ /secrets/WEBHOOK_SIGNING_SECRET }", "value": "{ /rawPayload }",
  "expected": "{ /headers/x-webhook-signature }" }
 
// Penyedia yang menerbitkan kunci dalam base64 (string base64 disimpan apa adanya di secrets)
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
  "secret": "{ /secrets/WEBHOOK_SIGNING_KEY }", "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. Di dalam value, tulis { /secrets/<key> } pada posisi tempat skema itu meletakkan kunci. Setiap skema menempatkan kunci di depan, di belakang, atau di tengah, dan dengan cara ini posisi mana pun dapat dinyatakan. Kuncinya dideklarasikan lebih dulu di secrets.
  • 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 + MERCHANT_KEY) sebagai hex huruf besar
{ "type": "Hash", "name": "expectedSign", "algorithm": "SHA256", "encoding": "HexUpper",
  "value": "{ /payload/orderId }{ /payload/amount }{ /secrets/MERCHANT_KEY }" }

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 tiga field pada engine ini yang bukan ekspresi nilai (sisanya adalah key pada Cache dan color pada PdfWatermark). 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 */ ] }