文件版本:1.1 建立日期:2026-05-03 最後更新:2026-05-04(合併 v2.1 迭代) 文件性質:SA — API 介面契約 對應 SD:design.md
| 日期 | 版本 | 變更摘要 |
|---|---|---|
| 2026-05-03 | 1.0 | 初版:parse / detail / confirm / list / delete 五端點 |
| 2026-05-04 | 1.1 | 合併 v2.1 三項迭代:(a) Step 1 parser-only — metadata 三欄改 optional 並可在 confirm 補;(b) 重新上傳 PDF 覆蓋既有版本(parse 帶 target_version_uid,confirm 走更新而非新建);(c) 合規框架版本即時編輯 — 4 個新端點(catalog-tree + group / control / assessment 各 PUT/DELETE) |
| # | Method | URL | Route Class | 說明 |
|---|---|---|---|---|
| 1 | GET | /api/1.0/oscal-framework-parse-jobs |
FrameworkParseJobListRoute |
列當前 tenant 進行中草稿(給 banner 用) |
| 2 | POST | /api/1.0/oscal-framework-parse-jobs/parse |
FrameworkParseJobParseRoute |
Step 1 — 上傳 PDF + 選 parser;metadata 可選(v1.1 起);可帶 target_version_uid 走覆蓋模式 |
| 3 | GET | /api/1.0/oscal-framework-parse-jobs/<uid> |
FrameworkParseJobDetailRoute |
取單筆 parse_job(含 parsed_result 給預覽) |
| 4 | POST | /api/1.0/oscal-framework-parse-jobs/<uid>/confirm |
FrameworkParseJobConfirmRoute |
Step 2 confirm — 套用 decisions/overrides + metadata;新建或覆蓋既有版本 |
| 5 | DELETE | /api/1.0/oscal-framework-parse-jobs/<uid> |
FrameworkParseJobDetailRoute |
軟刪 parse_job(is_active=false) |
| # | Method | URL | Route Class | 說明 |
|---|---|---|---|---|
| 6 | GET | /api/1.0/oscal-framework-version/<uid>/catalog-tree |
FrameworkVersionCatalogTreeRoute |
取版本 + groups + controls + assessments + has_references(給編輯頁載入) |
| 7 | PUT | /api/1.0/oscal-catalog-group/<uid> |
CatalogGroupRoute |
patch group 欄位(draft 限定) |
| 8 | DELETE | /api/1.0/oscal-catalog-group/<uid> |
CatalogGroupRoute |
cascade 刪 group + 子 controls + assessments(draft + 無引用) |
| 9 | PUT | /api/1.0/oscal-catalog-control/<uid> |
CatalogControlRoute |
patch control 欄位 |
| 10 | DELETE | /api/1.0/oscal-catalog-control/<uid> |
CatalogControlRoute |
cascade 刪 control + 子 assessments |
| 11 | PUT | /api/1.0/oscal-catalog-control-assessment/<uid> |
CatalogControlAssessmentRoute |
patch assessment 欄位 |
| 12 | DELETE | /api/1.0/oscal-catalog-control-assessment/<uid> |
CatalogControlAssessmentRoute |
刪 assessment |
端點命名遵循「複數集合 + 單筆 detail」慣例(見 memory)。Parse Job 沿用
/api/1.0/oscal-framework-*prefix;Catalog edit 則使用既有的 OSCAL 單體資源 prefix。
/api/1.0/oscal-framework-parse-jobs — 列當前 tenant 草稿用途:FE 進入匯入頁面時取得 banner 用的「未完成草稿」清單。
認證:JWT Required
Query Parameters(皆可選):
| 欄位 | 型別 | 說明 |
|---|---|---|
status |
string | 過濾 status,不傳預設只列 awaiting_review + failed |
is_mine |
boolean | true 只回傳當前使用者建立的;false/不傳回全部 tenant 內的 |
Response(FrameworkParseJobListResponseSchema):
{
"status": true,
"data": [
{
"uid": "uuid",
"status": "awaiting_review|failed",
"file_name": "string",
"target_framework_uid": "uuid",
"target_framework_name": "string",
"target_version": "string",
"target_release_date": "YYYY-MM-DD",
"is_mine": true,
"created_user": "string",
"created_user_name": "string",
"created_at": "ISO8601",
"updated_at": "ISO8601",
"summary": {
"groups": 6,
"controls": 110,
"assessments": 320
}
}
]
}
summary欄位由 BE 對parsed_result做 count,方便 FE banner 顯示「N 個 groups / M 個 controls」。target_framework_name由 BE 對 framework domain service 做 lookup,遵循審計欄位回傳規範(login_name 同時帶 nickname)。
/api/1.0/oscal-framework-parse-jobs/parse — 上傳並解析用途:Step 1 上傳 PDF + 選 parser。同步解析 PDF(不開背景 job),回傳 parse_uid 與 status。Metadata(version / release_date / publish_status)改為可選 — 預設於 Step 2 確認時填寫;若帶 target_version_uid 則走「重新上傳到既有版本」覆蓋模式。
認證:JWT Required
Content-Type:multipart/form-data
Form fields:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
file |
binary | Y | PDF binary |
json |
string (JSON) | Y | metadata payload,見下表 |
json payload 結構(FrameworkParseRequestSchema):
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
target_framework_uid |
string (uuid) | Y | 目標 framework |
parser_type |
string | Y | PDF 解析器;現行 dropdown 值 |
target_version |
string | N | 目標版本字串(v1.1 改可選;改在 confirm 補) |
target_release_date |
string (YYYY-MM-DD) | N | 發行日期(v1.1 改可選) |
target_publish_status |
string | N | draft / published / deprecated(v1.1 改可選) |
target_parent_version_uid |
string (uuid) | N | 若新增子版本 |
target_version_uid |
string (uuid) | N | v1.1 新增 — 若帶值表示「重新上傳覆蓋既有版本」。BE 預檢:版本存在、屬同 framework、publish_status='draft' 且無引用;自動把既有版本的 version / release_date / publish_status 帶進 parse_job 的 target_*(FE 顯示「重新上傳到 vX.Y」badge 用) |
Response(FrameworkParseResponseSchema):
{
"status": true,
"data": {
"uid": "uuid",
"status": "awaiting_review|failed",
"error_code": null,
"error_message": null,
"summary": {
"groups": 6,
"controls": 110,
"assessments": 320
}
}
}注意:解析失敗時 HTTP 仍回 200,body status 改 failed 並帶 error 欄位 — 由 FE 統一導去 detail 頁面再依 status 渲染(見 design 4.2)。
Error Cases:
| 條件 | HTTP | error code |
|---|---|---|
target_framework_uid 不存在 / 跨 tenant |
404 | GRC_FRAMEWORK_NOT_FOUND |
target_parent_version_uid 不屬同 framework |
400 | GRC_FRAMEWORK_PARSE_JOB_INVALID_PARENT |
target_version_uid 不存在 / 跨 framework |
404 | GRC_FRAMEWORK_NOT_FOUND |
target_version_uid 對應版本不是 draft |
403 | GRC_FRAMEWORK_VERSION_NOT_DRAFT |
target_version_uid 對應版本已被引用 |
409 | GRC_FRAMEWORK_VERSION_HAS_REFERENCES |
| 檔案缺失 / 非 PDF | 400 | GRC_FRAMEWORK_PARSE_FILE_INVALID |
| 檔案 > 50 MB | 413 | (Flask MAX_CONTENT_LENGTH) |
v1.1 變更:版本重名檢查(
(target_framework_uid, target_version)衝突)從 parse 移到 confirm(因為 parse 階段 version 可能還沒填)。
/api/1.0/oscal-framework-parse-jobs/<uid> — 取單筆用途:Step 2 預覽頁載入;或從草稿 banner 點繼續。
認證:JWT Required
Path Parameters:uid(parse_job uid)
Response(FrameworkParseJobDetailResponseSchema):
{
"status": true,
"data": {
"uid": "uuid",
"status": "awaiting_review|failed|completed|expired",
"file_name": "string",
"file_size": 123456,
"parser_type": "string",
"target_framework_uid": "uuid",
"target_framework": {
"uid": "...", "code": "...", "name": "..."
},
"target_version": "string|null",
"target_release_date": "YYYY-MM-DD|null",
"target_publish_status": "string|null",
"target_parent_version_uid": "uuid|null",
"target_version_uid": "uuid|null",
"file_uid": "string|null",
"parsed_result": {
"catalog": {
"name": "string",
"description": "string"
},
"groups": [
{
"uid": "uuid (parse-time generated)",
"name": "string",
"description": "string",
"order_no": 1,
"parent_group_uid": "uuid|null"
}
],
"controls": [
{
"uid": "uuid",
"control_id": "AC.L1-3.1.1",
"control_title": "string",
"description": "string",
"guidance": "string",
"order_no": 1,
"group_uid": "uuid"
}
],
"assessments": [
{
"uid": "uuid",
"name": "string",
"description": "string",
"order_no": 1,
"control_uid": "uuid"
}
]
},
"error_code": "string|null",
"error_message": "string|null",
"result_framework_version_uid": "uuid|null",
"is_active": true,
"created_user": "string",
"created_user_name": "string",
"updated_user": "string",
"updated_user_name": "string",
"created_at": "ISO8601",
"updated_at": "ISO8601"
}
}Error Cases:
| 條件 | HTTP | error code |
|---|---|---|
| uid 不存在或屬其他 tenant | 404 | GRC_FRAMEWORK_PARSE_JOB_NOT_FOUND |
| TTL 過期(is_active=false 且 status=expired) | 404 | 同上 |
/api/1.0/oscal-framework-parse-jobs/<uid>/confirm — 確認匯入用途:套用 FE 編輯(decisions + overrides),由 BE 寫入 catalog 系列正式表。依 parse_job 是否帶 target_version_uid 分流:
target_version_uid 為 null):建立新 framework_version;version 重名檢查在此進行target_version_uid 有值):保留版本 uid 不變、舊 catalog 標 DEPRECATED、新 catalog 接到同 framework_version、更新 file_uid認證:JWT Required
Path Parameters:uid
Request Body(FrameworkParseJobConfirmRequestSchema):
{
"target_version": "string (v1.1 新增;新建模式必填)",
"target_release_date": "YYYY-MM-DD (v1.1 新增;新建模式必填)",
"target_publish_status": "string (v1.1 新增;新建模式必填)",
"decisions": [
{ "type": "group", "uid": "uuid", "action": "delete" },
{ "type": "control", "uid": "uuid", "action": "delete" },
{ "type": "assessment", "uid": "uuid", "action": "delete" }
],
"overrides": {
"groups": [
{
"uid": "uuid",
"name": "string|null",
"description": "string|null",
"order_no": 1,
"parent_group_uid": "uuid|null"
}
],
"controls": [
{
"uid": "uuid",
"control_id": "string|null",
"control_title": "string|null",
"description": "string|null",
"guidance": "string|null",
"order_no": 1,
"group_uid": "uuid"
}
],
"assessments": [
{
"uid": "uuid",
"name": "string|null",
"description": "string|null",
"order_no": 1,
"control_uid": "uuid"
}
]
}
}規則:
decisions[].action 目前僅支援 delete(未來若開放新增則加 create)overrides[*].uid 必須對應到 parsed_result 內、且未被 decisions 標 delete 的條目overrides[*] 內的字串欄位若為 null 表示「不覆蓋」;其他欄位(order_no / parent_group_uid / group_uid / control_uid)必為當前實際值或新值parent_group_uid / group_uid / control_uid 不可指向被 delete 的 uidGRC_FRAMEWORK_PARSE_JOB_METADATA_REQUIREDResponse(FrameworkParseJobConfirmResponseSchema):
{
"status": true,
"data": {
"framework_version_uid": "uuid",
"framework_uid": "uuid",
"version": "string",
"groups_created": 6,
"controls_created": 108,
"assessments_created": 312
}
}Error Cases:
| 條件 | HTTP | error code |
|---|---|---|
| parse_job 不存在 / TTL 過期 | 404 | GRC_FRAMEWORK_PARSE_JOB_NOT_FOUND |
status != awaiting_review |
409 | GRC_FRAMEWORK_PARSE_JOB_ALREADY_CONFIRMED |
| Metadata 三欄都沒有來源(v1.1) | 400 | GRC_FRAMEWORK_PARSE_JOB_METADATA_REQUIRED |
| 套用後剩餘 controls = 0 | 400 | GRC_FRAMEWORK_PARSE_JOB_EMPTY |
| 引用已 delete 的 uid | 400 | GRC_FRAMEWORK_PARSE_JOB_INVALID_REFERENCE |
| 寫入時 (framework_id, version) 衝突(新建模式) | 409 | GRC_FRAMEWORK_VERSION_ALREADY_EXISTS |
| 覆蓋模式下版本不再是 draft / 已被引用(race) | 403 / 409 | GRC_FRAMEWORK_VERSION_NOT_DRAFT / GRC_FRAMEWORK_VERSION_HAS_REFERENCES |
/api/1.0/oscal-framework-parse-jobs/<uid> — 軟刪 / 捨棄用途:使用者點「捨棄草稿」或關閉編輯頁時呼叫。
認證:JWT Required
Response:{ "status": true, "data": true }
業務邏輯:將 is_active=false / status='expired',不真正 delete row。
Error Cases:
| 條件 | HTTP | error code |
|---|---|---|
| parse_job 不存在 | 404 | GRC_FRAMEWORK_PARSE_JOB_NOT_FOUND |
status=completed(已成功匯入) |
409 | GRC_FRAMEWORK_PARSE_JOB_ALREADY_CONFIRMED |
/api/1.0/oscal-framework-version/<uid>/catalog-tree — 取版本 + 所有 catalog 條目(v1.1 新增)用途:合規框架版本即時編輯頁載入用。一次取版本 metadata + 全部 groups / controls / assessments + 引用狀態(has_references)。
Response:
{
"status": true,
"data": {
"version_uid": "uuid",
"version": "string",
"publish_status": "string",
"file_uid": "string|null",
"has_references": false,
"groups": [ { "uid": "uuid", "name": "...", "description": "...", "order_no": 1, "parent_group_uid": "uuid|null" } ],
"controls": [ { "uid": "uuid", "control_id": "...", "control_title": "...", "description": "...", "guidance": "...", "order_no": 1, "group_uid": "uuid" } ],
"assessments": [ { "uid": "uuid", "name": "...", "description": "...", "order_no": 1, "control_uid": "uuid" } ]
}
}/api/1.0/oscal-catalog-group/<uid> — Group 單筆編輯(v1.1 新增)雙軌守門:對應 framework_version 必須 publish_status='draft' 且 has_references=false。
{ name, description, order_no, parent_group_uid }(皆 optional,為 null 表示不變)/api/1.0/oscal-catalog-control/<uid> — Control 單筆編輯(v1.1 新增){ control_id, control_title, description, guidance, order_no, group_uid }/api/1.0/oscal-catalog-control-assessment/<uid> — Assessment 單筆編輯(v1.1 新增){ name, description, order_no, control_uid }§2.7~2.9 共通 Error Cases:
| 條件 | HTTP | error code |
|---|---|---|
| 對應版本不是 draft | 403 | GRC_FRAMEWORK_VERSION_NOT_DRAFT |
| 對應版本已被引用(DELETE 時) | 409 | GRC_FRAMEWORK_VERSION_HAS_REFERENCES |
| uid 不存在 | 404 | GRC_CATALOG_GROUP_NOT_FOUND / GRC_CATALOG_CONTROL_NOT_FOUND / GRC_CATALOG_CONTROL_ASSESSMENT_NOT_FOUND(沿用既有) |
# common/code/grc_error_code.py — Framework PDF Import 段
GRC_FRAMEWORK_PARSE_FILE_INVALID = ("上傳檔案無效,請上傳 PDF 檔案", "GRC_400020")
GRC_FRAMEWORK_PARSE_JOB_EMPTY = ("解析結果為空,無控制項可匯入", "GRC_400017")
GRC_FRAMEWORK_PARSE_JOB_INVALID_PARENT = ("Parent version 無效或跨 framework", "GRC_400018")
GRC_FRAMEWORK_PARSE_JOB_INVALID_REFERENCE = ("Override 引用了被刪除的條目", "GRC_400019")
GRC_FRAMEWORK_PARSE_JOB_METADATA_REQUIRED = ("確認匯入需指定版本 / 發布日期 / 發布狀態", "GRC_400021") # v1.1
GRC_FRAMEWORK_NOT_FOUND = ("合規框架不存在", "GRC_404026")
GRC_FRAMEWORK_PARSE_JOB_NOT_FOUND = ("框架解析任務不存在或已過期", "GRC_404025")
GRC_FRAMEWORK_VERSION_ALREADY_EXISTS = ("框架版本已存在(同 framework 下 version 重複)", "GRC_409006")
GRC_FRAMEWORK_PARSE_JOB_ALREADY_CONFIRMED = ("解析任務已被確認匯入或已捨棄", "GRC_412015")
GRC_FRAMEWORK_PARSE_FAILED = ("框架 PDF 解析失敗", "GRC_422004")
# Framework Version 即時編輯(v1.1 新增)
GRC_FRAMEWORK_VERSION_NOT_DRAFT = ("僅 draft 狀態的版本可編輯", "GRC_403030")
GRC_FRAMEWORK_VERSION_HAS_REFERENCES = ("版本已被合規資源庫引用,禁止刪除控制項 / 群組 / 評估目標", "GRC_409010")序號接續既有 grc_error_code.py 末段;422 採 SSP doc parser 同模式(FATAL parse error 用 422 區)。
route_capabilities 配置決定誰可呼叫)/compliance-framework/import-version 需在 ui_routes 註冊;對應的 BE capabilities 需新增至 route_capabilities(詳見 docs/system-design/permission/... 的 SOP)