前言
Mapping editor 要讓商家選「Shopify 訂單編號寫到 Ragic 哪個欄位」,首先得知道 Ragic 表單有哪些 Field ID、哪些欄位可寫、哪些是自動編號、哪些是 Link/Load 或子表格。
只靠手動輸入 Field ID 很容易打錯,也無法在 UI 顯示足夠脈絡。這個案例因此建立一份可貼入 App 的 Ragic API 文件,再由 parser 轉成表單與欄位 catalog,最後交給 Mapping editor 使用。
先說清楚邊界:這裡的「Ragic API 文件」是專案自訂的繁體中文 Markdown 模板,不是 Ragic 官方承諾可供任意程式解析的 schema endpoint,也不是把任何官方說明頁貼進來都能工作。
上一篇說明了 為什麼欄位 Mapping 最終改回人工確認。這篇往前追,看看人工 editor 的選項怎麼可靠產生。
原本的做法與問題症狀
最簡單的設定表單可以只有兩個文字欄位:
Shopify path: order.name
Ragic field: 700002
但這種 UI 把所有風險留給使用者:
- Field ID 可能不存在。
- Field ID 存在,但屬於另一張表單。
- 欄位是自動編號或唯讀 loaded field。
- 欄位位於子表格,不能用一般 top-level payload 寫入。
- 顯示名稱相同,但 type 或 write format 不相容。
- 表單 URL 指到另一個 environment 或不同 sheet。
若 App 只把這些字串存進資料庫,Dry Run 看起來甚至可能正常;直到真正送出,Ragic 才回傳錯誤,或更糟地把資料寫進不該覆寫的欄位。
問題是怎麼推敲出來的?
Ragic 官方文件說明 Field ID 是程式辨認欄位的無歧義方式。讀取資料時也可以指定 naming=EID,讓 JSON property 使用 Field ID,而不是可能重複或改名的 display name。
所以 editor 的 source of truth 不該是人腦記得的欄位名稱,而應是帶有 Field ID 與寫入屬性的 schema snapshot。
來源專案定義的文件會為每張表單列出:
- 表單名稱。
- 表單 URL 與 API URL。
- 主表單 key。
- Field Name、Field ID、Type。
- Writable、Write Format、Memo。
- 子表格 heading 與 subtable key。
Parser 逐行辨認固定 heading 與 Markdown table。遇到 field row 時,會根據 Writable 與 Memo 判斷 readonly,從 type 判斷 linked field,從 memo 判斷 loaded field,並抽出單選選項與目前的 subtable key。
這份 normalized schema 才是後續 editor 的輸入。
最後採用的架構
整體流程分成四層:
Ragic 表單設計
→ 產生專案格式的 API 文件
→ parser 轉成 RagicParsedSheet[]
→ 篩選可寫入欄位
→ Shopify catalog × Ragic options
→ 商家建立 manual mapping
Parser 保留完整欄位資訊,但 editor 會再縮減:
writable必須為 true。isReadonly必須為 false。isLoadedField必須為 false。- 選項顯示 Field Name、Field ID 與 type。
儲存時不能只信 UI 已篩選過。Server 會重新從同一份 parsed schema 建立 writableFieldsById,任何不在集合內的值都拒絕。
Shopify 端也不是自由輸入 path,而是由 order/customer field catalog 提供固定選項。兩邊都使用 allow-list,Mapping 才不會變成任意讀取 JSON path、任意寫入 Field ID 的通道。
關鍵實作
Parser 的核心不是複雜語法樹,而是小而嚴格的狀態機:
for (const rawLine of lines) {
const line = rawLine.trim();
if (isSheetHeading(line)) startSheet(line);
else if (isApiUrl(line)) currentSheet.apiUrl = readValue(line);
else if (isSubtableHeading(line)) activeSubtableKey = readKey(line);
else if (isFieldHeader(line)) inFieldTable = true;
else if (inFieldTable && isFieldRow(line)) {
currentSheet.fields.push(parseFieldRow(line, activeSubtableKey));
}
}
解析後的安全欄位可以長成:
type ParsedField = {
id: string;
name: string;
type: string;
writable: boolean;
isReadonly: boolean;
isLinkedField: boolean;
isLoadedField: boolean;
subtableKey?: string;
options: string[];
};
Editor 選項使用 Field ID 當 value,display name 只用於說明:
const options = sheet.fields
.filter((field) =>
field.writable &&
!field.isReadonly &&
!field.isLoadedField
)
.map((field) => ({
value: field.id,
label: `${field.name} (${field.id}) · ${field.type}`,
}));
文章示例使用假 Field ID 與假表單,不包含正式 Ragic URL、客戶 schema 或 API key。
實際驗證
來源 parser 測試已覆蓋:
- 同一份文件解析多張 sheet。
- Sheet name、form URL、API URL 與 main key。
- 一般可寫文字欄位。
- 自動編號與唯讀欄位。
- 單選選項的抽取。
- Link field 與 loaded field 的區別。
- Subtable heading 後欄位會保留 subtable key。
- 沒有合法 sheet section 時回傳空陣列。
Manual mapping 測試另外覆蓋:
- 只接受 Shopify catalog 中存在的 path。
- 只接受 parser 證明可寫的 Ragic Field ID。
- 商家選擇不送出的欄位不會進 Mapping。
- 缺少必要 unique key 時拒絕儲存。
這些測試證明 parser 與 editor 遵守目前文件 contract;它們不能證明任意版本、任意語言或人工修改過的 Ragic 文件都能被正確解析。
仍然存在的限制
第一個限制是格式耦合。Parser 依賴固定的繁體中文 heading、欄位順序與 Markdown table。只要文件產生器改成英文、調整欄名或改用 JSON,舊 parser 可能回傳空結果。較成熟的作法是替文件格式加明確 version,並以 fixture contract test 鎖住變更。
第二個限制是「看得到 subtable」不等於「會寫 subtable」。Ragic 官方的 create/update 說明要求 subtable row identity;新增 row 常以負數 temporary row ID 分組,更新既有 row 則要使用實際 row ID。目前案例的 payload builder 主要處理 top-level field,不能因 parser 保存了 subtableKey 就宣稱支援完整子表格同步。
第三個限制是 Link/Load 與公式。欄位可寫入不代表寫完後會自動得到預期的載入值;Ragic 的 doLinkLoad、doFormula、workflow 與 record lock 都是額外行為,應在 mapping policy 或 write profile 明確設定。
最後,Field ID 雖適合程式辨認,Ragic 官方也提醒不要任意修改。若 schema 真的調整,App 應將舊 Mapping 標記為 needs review,而不是只靠 display name 自動搬移。
可以延伸到哪些情境?
這個方法不只適用 Ragic:
- 先把外部系統 schema 轉成版本化 normalized model。
- UI 只呈現 server 證明可選的欄位。
- 儲存時再次做 allow-list 驗證。
- 顯示名稱供人理解,穩定 ID 才是程式識別。
- 子表、關聯、公式與 workflow 使用不同 capability 表示,不用一個
writable布林值概括。
下一篇會使用這份 Mapping 做受控預覽:寫入 Ragic 前先 Dry Run:資料同步工具如何讓商家安全確認?
參考資料
- Ragic:API Developer Guide
- Ragic:Finding the Field ID for a Field
- Ragic:Field Naming
- Ragic:Returned Data JSON Format
- Ragic:Creating a New Entry
- Ragic:Modifying an Entry
- Ragic:Create / Update Parameters
以上平台文件查核日期:2026-07-26。
