Skip to Content
核心概念JSONL IaC用 REST 補完

IaC 宣告不了的部分,以及怎麼用 REST 補完

一份 JSONL 文件可以宣告系統的大部分,但不是全部。有六項設定沒有對應的行類型,或是雖然有、卻只能寫下綁環境的值,所以一次完整的部署是「一份文件」加上「一小串 REST 呼叫」。

這一頁就是那串呼叫。每一項都會說要跑什麼、相對於 plan / apply 該在什麼時候跑,以及最容易踩到的那件事:之後這個設定由哪一邊擁有,因為只要文件也宣告了同一件事,重新 apply 就會直接蓋掉。

清單

設定文件為什麼裝不了怎麼補
require_approval 由誰核准規則引用的是覆核範本的原始 id,而範本與群組屬於覆核模組先建立覆核範本
Callback token沒有行類型。密鑰只顯示一次,無法在文件裡來回apply 之後鑄造
帶密鑰的公開讀取權杖public_read v1 只支援無密鑰,理由同上apply 之後鑄造
跨表的 create_record / update_record 動作它的 table_id 必須是線上 id;只有 materialize_slots 接受 $table:<ref>讀出 id,再寫回文件
系統 tag 以外的 tag沒有 tag 行類型;command.tag_id 只吃同 scope 的原始 id先建立 tag
移除 client_access 設定這個 kind 沒有 state,所以沒有 absent 可宣告在文件裡停用它

1. 先建立覆核範本,再第一次 apply

require_approval 規則如果沒有一個能解析到同公司、可用範本的 template_id,根本存不進去,所以這是前置條件,不是後續補充。先建立人的群組,再建立以它為關卡的範本:

# a) 覆核人員 curl -X POST "$BASE_URL/private/module/review/groups" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{ "name": "訂單覆核人員", "description": "負責審核高金額訂單", "member_ids": ["<user-uuid>"] }' # b) 以該群組為關卡的範本 curl -X POST "$BASE_URL/private/module/review/templates" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{ "name": "高金額訂單覆核", "description": "單一關卡", "gates": [{ "name": "主管覆核", "condition": { "type": "group", "group_id": "<group-uuid>", "mode": "any" } }], "require_signature": false }'

然後把回傳的範本 id 貼進規則那一行:

{"kind":"rule","table":"orders","ref":"big_orders_need_review","spec":{"type":"require_approval","events":["created","updated"],"template_id":"<template-uuid>","when":[{"column":"total","op":"gt","value":100000}]}}

擁有權: 規則屬於文件,範本屬於覆核模組。重新 apply 不會動到範本,但會重新套用規則,所以檔案裡那個 id 才是算數的那個。

搬到別的環境: 那個範本 id 在別的環境不存在。請先在目標環境建立範本,apply 之前只改這一個值。這是整個格式裡最不可攜的一行,所以請把核准規則放在你預期會逐環境修改的小文件裡,不要埋在上百行的大 bundle 中間。

review.groups.createreview.templates.create

2. apply 之後鑄造 callback token

資料表要先存在才會有權杖,所以這是 apply 之後的步驟:

curl -X POST "$BASE_URL/private/module/custom_tables/chatroom/$CHATROOM_ID/tables/$TABLE_ID/callback-tokens" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{ "name": "訂單系統回呼", "allowed_ops": "create,update", "valid_until": null }'

201 只會帶一次 secret。請立刻放進密鑰管理系統;列表路由不會再給你。

擁有權: 完全屬於 REST。沒有行類型就沒有 drift,重新 apply 也撤不掉它。這是雙面的:export 同樣不會輸出它,所以權杖在你的文件裡是看不見的,必須另外記錄。

callbackTokens.mint外部回呼,包含公開表單用來長出欄位的 form-schema 路由。

3. apply 之後鑄造帶密鑰的公開讀取權杖

IaC 可以宣告無密鑰的公開權杖,也就是 URL 本身就是憑證。如果你要 bearer 密鑰,請用 REST 鑄造:

curl -X POST "$BASE_URL/private/module/custom_tables/chatroom/tables/$TABLE_ID/public-read-tokens" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{ "name": "夥伴資料流", "view_id": "<view-uuid>", "secretless": false, "visible_columns": ["品項", "單價"], "rpm": 120 }'

注意這個路徑沒有 scope id 那一段。檢視必須已經存在,所以先 apply 文件,再從 IaC state 讀出 view id(做法見第 4 步)。

擁有權: 屬於 REST,而 IaC 永遠不會接管它。用同一個 ref 寫一行 public_read 只會再鑄一個另一個權杖,不會把這個接過去。同一個已公開的檢視,請只選一條路。

publicRead.mint公開讀取

4. 解出跨表觸發器的目標

會寫入另一張表的觸發器動作需要那張表的線上 id。只有 materialize_slots 接受 $table:<ref>,所以 create_recordupdate_record 的做法是:先 apply 一次、讀出 id、寫回文件。

# 這份文件管理的每一個資源的 state 列 curl "$BASE_URL/private/module/custom_tables/chatroom/$CHATROOM_ID/tables/iac/state?system=fulfilment" \ -H "Authorization: Bearer $TOKEN"
{ "rows": [ { "system": "fulfilment", "resource_type": "table", "ref": "shipments", "resource_id": "<table-uuid>", "table_id": null } ], "total": 1 }

取你要的那個 ref 對應的 resource_id,寫進動作裡:

{"kind":"trigger","table":"orders","ref":"open_shipment","spec":{"name":"開立出貨單","on":"created","actions":[{"type":"create_record","table_id":"<table-uuid>","data":{"訂單":"$row.訂單編號"}}]}}

擁有權: 觸發器屬於文件。請不要用 REST PUT 觸發器來「修好」這件事:下一次 plan 會把你的 REST 修改視為 drift 並改回去。那個 id 該待在檔案裡。

iac.statetriggers.set

5. 先建立非系統 tag,再第一次 apply

header 的 system 會自動管理一個 tag,並把文件裡的資料表都掛上去。對大多數 bundle 這就夠了,包含 command:省略 tag_id 時它會綁到系統 tag。如果你需要另一個 tag,請用 REST 建立並掛表:

curl -X POST "$BASE_URL/private/module/custom_tables/chatroom/$CHATROOM_ID/table-tags" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{ "name": "營運", "description": "日常營運相關資料表", "color": "#2563eb" }' curl -X POST "$BASE_URL/private/module/custom_tables/chatroom/$CHATROOM_ID/table-tags/$TAG_ID/tables/$TABLE_ID" \ -H "Authorization: Bearer $TOKEN"

擁有權: 系統 tag 以外的 tag 都屬於 REST。Command 指名了它就會讓文件綁住那個環境,和範本 id 一樣,所以能省略 tag_id 就省略。

tags.createtags.assignTable

6. 結束 client access

client_access 沒有 state 欄位,所以沒有 absent 那一行可以寫。請改成在文件裡停用它:

{"kind":"client_access","table":"orders","spec":{"passphrase_enabled":false}}

Export 一律把 passphrase 寫回 "<REDACTED>"。在同一個 scope apply 這個 redacted 值會保留既有 hash;在沒有 live hash 的新 scope 啟用時,換成真實 passphrase 之前都會是 plan 錯誤。

順序整理

面對一個全新的環境:

  1. 為每一條 require_approval 規則建立覆核群組與範本,以及任何非系統 tag,然後把這些 id 換進文件。
  2. plan、讀差異、apply
  3. IaC state 取得你需要的線上 id,把跨表觸發器的目標寫回文件。
  4. plan 一次,應該是乾淨的。第二次 plan 還有變更表示文件裡有東西沒有收斂,怎麼讀那個結果見 plan 與 apply
  5. 鑄造 callback token 與需要密鑰的公開讀取權杖,並保存它們的密鑰。 第 1 步與第 3 步是讓文件變成「綁環境」的兩件事。格式裡其他東西都可以原樣搬動,因為授權、洞察系統選擇與身分欄位的儲存格都接受可攜身分代號
Last updated on