diff --git a/models_gen.go b/models_gen.go index 76479c8..6f86559 100644 --- a/models_gen.go +++ b/models_gen.go @@ -4993,6 +4993,38 @@ type Flapping struct { MuteMins int64 `json:"mute_mins,omitempty" toon:"mute_mins,omitempty"` } +// FolderCreateRequest is generated from the Flashduty OpenAPI schema. +type FolderCreateRequest struct { + // Folder name; cannot contain a period, slash, or comma. + Name string `json:"name" toon:"name"` + // Free-form note; omitted or empty clears the note on update. + Note string `json:"note,omitempty" toon:"note,omitempty"` + // Parent folder ID; 0 or omitted creates a root folder. + ParentID uint64 `json:"parent_id,omitempty" toon:"parent_id,omitempty"` + // Team ID; 0 means account-wide. Omitted on update resets to 0. + TeamID uint64 `json:"team_id,omitempty" toon:"team_id,omitempty"` +} + +// FolderDeleteRequest is generated from the Flashduty OpenAPI schema. +type FolderDeleteRequest struct { + // Folder ID. + ID uint64 `json:"id" toon:"id"` +} + +// FolderHealthItem is generated from the Flashduty OpenAPI schema. +type FolderHealthItem struct { + // Folder ID. + FolderID uint64 `json:"folder_id" toon:"folder_id"` + // Whether this folder or a visible descendant has active alerts or rule runtime issues. + HasIssue bool `json:"has_issue" toon:"has_issue"` +} + +// FolderHealthOutput is generated from the Flashduty OpenAPI schema. +type FolderHealthOutput struct { + // Folder health summaries. + Items []FolderHealthItem `json:"items" toon:"items"` +} + // FolderItem is generated from the Flashduty OpenAPI schema. type FolderItem struct { // Owning account ID. @@ -5023,6 +5055,26 @@ type FolderItem struct { UpdaterName string `json:"updater_name" toon:"updater_name"` } +// FolderMoveRequest is generated from the Flashduty OpenAPI schema. +type FolderMoveRequest struct { + // Destination parent folder ID; required even when 0 to move to the root. + DestParentID uint64 `json:"dest_parent_id" toon:"dest_parent_id"` + // Folder ID. + ID uint64 `json:"id" toon:"id"` +} + +// FolderUpdateRequest is generated from the Flashduty OpenAPI schema. +type FolderUpdateRequest struct { + // Folder ID. + ID uint64 `json:"id" toon:"id"` + // Folder name; cannot contain a period, slash, or comma. + Name string `json:"name" toon:"name"` + // Free-form note; omitted or empty clears the note on update. + Note string `json:"note,omitempty" toon:"note,omitempty"` + // Team ID; 0 means account-wide. Omitted on update resets to 0. + TeamID uint64 `json:"team_id,omitempty" toon:"team_id,omitempty"` +} + // GetIntegrationRequest is generated from the Flashduty OpenAPI schema. type GetIntegrationRequest struct { // Integration ID. diff --git a/openapi/openapi.en.json b/openapi/openapi.en.json index 83b94f7..95a3c2e 100644 --- a/openapi/openapi.en.json +++ b/openapi/openapi.en.json @@ -33311,6 +33311,140 @@ "example": "0123456789abcdef0123456789abcdef100" } } + }, + "FolderCreateRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "name": { + "type": "string", + "description": "Folder name; cannot contain a period, slash, or comma.", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "Free-form note; omitted or empty clears the note on update." + }, + "parent_id": { + "type": "integer", + "description": "Parent folder ID; 0 or omitted creates a root folder.", + "format": "uint64", + "minimum": 0 + }, + "team_id": { + "type": "integer", + "description": "Team ID; 0 means account-wide. Omitted on update resets to 0.", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "name" + ] + }, + "FolderUpdateRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 1 + }, + "name": { + "type": "string", + "description": "Folder name; cannot contain a period, slash, or comma.", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "Free-form note; omitted or empty clears the note on update." + }, + "team_id": { + "type": "integer", + "description": "Team ID; 0 means account-wide. Omitted on update resets to 0.", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "name" + ] + }, + "FolderMoveRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 1 + }, + "dest_parent_id": { + "type": "integer", + "description": "Destination parent folder ID; required even when 0 to move to the root.", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "dest_parent_id" + ] + }, + "FolderDeleteRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 1 + } + }, + "required": [ + "id" + ] + }, + "FolderHealthItem": { + "type": "object", + "description": "Health summary for a visible folder.", + "properties": { + "folder_id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 0 + }, + "has_issue": { + "type": "boolean", + "description": "Whether this folder or a visible descendant has active alerts or rule runtime issues." + } + }, + "required": [ + "folder_id", + "has_issue" + ] + }, + "FolderHealthOutput": { + "type": "object", + "description": "Health summaries for visible folders.", + "properties": { + "items": { + "type": "array", + "description": "Folder health summaries.", + "items": { + "$ref": "#/components/schemas/FolderHealthItem" + } + } + }, + "required": [ + "items" + ] } }, "securitySchemes": { @@ -67177,7 +67311,7 @@ "Monitors/Rule folders" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | **Alerting Rules Read** or **Node Permissions Read** (`monit`) |\n\n## Usage\n\n- The response is a flat list of every readable folder, not a tree: rebuild the hierarchy from `parent_id` and `parent_path`, and search client-side.\n- Dashboards and alert rules both live in this folder tree, so the same IDs appear as `folder_id` on dashboard payloads.\n- Folder writes (`create`, `update`, `move`, `delete`) are still JWT-only and are not part of the public API.", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | **Alerting Rules Read** or **Node Permissions Read** (`monit`) |\n\n## Usage\n\n- The response is a flat list of every readable folder, not a tree: rebuild the hierarchy from `parent_id` and `parent_path`, and search client-side.\n- Dashboards and alert rules both live in this folder tree, so the same IDs appear as `folder_id` on dashboard payloads.", "href": "/en/api-reference/monitors/rule-folders/monit-folder-read-list", "metadata": { "sidebarTitle": "List monitor folders" @@ -68006,6 +68140,435 @@ } } } + }, + "/monit/folder/health": { + "post": { + "operationId": "monit-folder-read-health", + "summary": "Get monitor folder health", + "description": "Get health summaries for visible folders, including issues in their descendants.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **30 requests/minute**; **2 requests/second** per account |\n| Permissions | **Alerting Rules Read**, **Alerting Rules Manage**, **Node Permissions Read**, or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Issues include active alerts and alert-rule runtime problems; issues propagate to visible ancestors.\n- This operation scans folder rules and runtime state. Avoid frequent polling.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-read-health", + "metadata": { + "sidebarTitle": "Get monitor folder health" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderHealthOutput" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "folder_id": 12, + "has_issue": false + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/monit/folder/create": { + "post": { + "operationId": "monit-folder-write-create", + "summary": "Create monitor folder", + "description": "Create a monitor folder at the root or under an accessible parent folder.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Use `parent_id: 0` or omit it to create a root folder. The parent must be accessible.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-create", + "metadata": { + "sidebarTitle": "Create monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout", + "note": "Checkout service folders", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderCreateRequest" + }, + "example": { + "name": "Checkout", + "note": "Checkout service folders", + "parent_id": 10, + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/update": { + "post": { + "operationId": "monit-folder-write-update", + "summary": "Update monitor folder", + "description": "Replace the name, note, and team assignment of an accessible monitor folder.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Send both `id` and `name`. This is a replacement of editable fields: omitting `note` clears it, and omitting `team_id` resets it to 0.\n- Use the move operation to change the parent folder.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-update", + "metadata": { + "sidebarTitle": "Update monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout production", + "note": "Production services", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderUpdateRequest" + }, + "example": { + "id": 12, + "name": "Checkout production", + "note": "Production services", + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/move": { + "post": { + "operationId": "monit-folder-write-move", + "summary": "Move monitor folder", + "description": "Move a monitor folder and its descendants under another folder or to the root.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- You must have access to both the source and destination folders.\n- `dest_parent_id` is required; send 0 explicitly to move to the root. Moving under the folder itself or a descendant is rejected.\n- Descendant paths are updated in the same transaction.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-move", + "metadata": { + "sidebarTitle": "Move monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Empty object." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderMoveRequest" + }, + "example": { + "id": 12, + "dest_parent_id": 0 + } + } + } + } + } + }, + "/monit/folder/delete": { + "post": { + "operationId": "monit-folder-write-delete", + "summary": "Delete monitor folder", + "description": "Delete an accessible monitor folder after removing its children, rules, and dashboards.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Only empty folders can be deleted. Child folders, alert rules, and dashboards block deletion with HTTP 409 and error code `FolderNotEmpty`.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-delete", + "metadata": { + "sidebarTitle": "Delete monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Empty object." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + }, + "409": { + "description": "Folder is not empty.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "description": "Request ID." + }, + "error": { + "type": "object", + "description": "Error details.", + "properties": { + "code": { + "type": "string", + "description": "FolderNotEmpty: remove the folder contents before retrying.", + "enum": [ + "FolderNotEmpty" + ] + }, + "message": { + "type": "string", + "description": "Error message." + } + }, + "required": [ + "code", + "message" + ] + } + }, + "required": [ + "request_id", + "error" + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "FolderNotEmpty", + "message": "Folder contains dashboards" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderDeleteRequest" + }, + "example": { + "id": 12 + } + } + } + } + } } }, "security": [ diff --git a/openapi/openapi.zh.json b/openapi/openapi.zh.json index 06f4462..99adf48 100644 --- a/openapi/openapi.zh.json +++ b/openapi/openapi.zh.json @@ -33311,6 +33311,140 @@ "example": "0123456789abcdef0123456789abcdef100" } } + }, + "FolderCreateRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "name": { + "type": "string", + "description": "目录名称;不能包含句点、斜杠或逗号。", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "备注;更新时省略或传空字符串会清空备注。" + }, + "parent_id": { + "type": "integer", + "description": "父目录 ID;0 或省略时创建顶层目录。", + "format": "uint64", + "minimum": 0 + }, + "team_id": { + "type": "integer", + "description": "团队 ID;0 表示账户范围。更新时省略会重置为 0。", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "name" + ] + }, + "FolderUpdateRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 1 + }, + "name": { + "type": "string", + "description": "目录名称;不能包含句点、斜杠或逗号。", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "备注;更新时省略或传空字符串会清空备注。" + }, + "team_id": { + "type": "integer", + "description": "团队 ID;0 表示账户范围。更新时省略会重置为 0。", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "name" + ] + }, + "FolderMoveRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 1 + }, + "dest_parent_id": { + "type": "integer", + "description": "目标父目录 ID;移至顶层时必须显式传 0。", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "dest_parent_id" + ] + }, + "FolderDeleteRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 1 + } + }, + "required": [ + "id" + ] + }, + "FolderHealthItem": { + "type": "object", + "description": "目录健康摘要。", + "properties": { + "folder_id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 0 + }, + "has_issue": { + "type": "boolean", + "description": "本目录或可见后代目录是否存在活跃告警或规则运行问题。" + } + }, + "required": [ + "folder_id", + "has_issue" + ] + }, + "FolderHealthOutput": { + "type": "object", + "description": "可见目录的健康摘要。", + "properties": { + "items": { + "type": "array", + "description": "目录摘要列表。", + "items": { + "$ref": "#/components/schemas/FolderHealthItem" + } + } + }, + "required": [ + "items" + ] } }, "securitySchemes": { @@ -67177,7 +67311,7 @@ "Monitors/规则分组" ], "x-mint": { - "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | **1,000 requests/minute**; **50 requests/second** per account |\n| 权限要求 | **告警规则查看** 或 **节点权限查看**(`monit`) |\n\n## 使用说明\n\n- 响应是可读目录的扁平列表而非树:请用 `parent_id` 与 `parent_path` 重建层级,并在客户端自行搜索。\n- 仪表盘与告警规则共用这棵目录树,因此同一批 ID 会作为仪表盘载荷中的 `folder_id` 出现。\n- 目录写操作(`create`、`update`、`move`、`delete`)仍仅支持 JWT,不在公开 API 范围内。", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | **1,000 requests/minute**; **50 requests/second** per account |\n| 权限要求 | **告警规则查看** 或 **节点权限查看**(`monit`) |\n\n## 使用说明\n\n- 响应是可读目录的扁平列表而非树:请用 `parent_id` 与 `parent_path` 重建层级,并在客户端自行搜索。\n- 仪表盘与告警规则共用这棵目录树,因此同一批 ID 会作为仪表盘载荷中的 `folder_id` 出现。", "href": "/zh/api-reference/monitors/rule-folders/monit-folder-read-list", "metadata": { "sidebarTitle": "查询监控节点列表" @@ -68006,6 +68140,435 @@ } } } + }, + "/monit/folder/health": { + "post": { + "operationId": "monit-folder-read-health", + "summary": "获取监控目录健康摘要", + "description": "获取可见目录的健康摘要,包含后代目录中的问题。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **30 次/分钟**;**2 次/秒** |\n| 权限 | **告警规则查看**、**告警规则管理**、**节点权限查看**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 问题包括活跃告警和告警规则运行问题,并向可见祖先目录汇总。\n- 此操作扫描目录规则和运行状态,请避免频繁轮询。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-read-health", + "metadata": { + "sidebarTitle": "获取监控目录健康摘要" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderHealthOutput" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "folder_id": 12, + "has_issue": false + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/monit/folder/create": { + "post": { + "operationId": "monit-folder-write-create", + "summary": "创建监控目录", + "description": "在顶层或有权访问的父目录下创建监控目录。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 传入 `parent_id: 0` 或省略该字段以创建顶层目录。您必须有权访问父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-create", + "metadata": { + "sidebarTitle": "创建监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout", + "note": "Checkout service folders", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderCreateRequest" + }, + "example": { + "name": "Checkout", + "note": "Checkout service folders", + "parent_id": 10, + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/update": { + "post": { + "operationId": "monit-folder-write-update", + "summary": "更新监控目录", + "description": "替换有权访问的监控目录的名称、备注和团队归属。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 必须传入 `id` 和 `name`。此操作替换可编辑字段:省略 `note` 会清空备注,省略 `team_id` 会重置为 0。\n- 请使用移动接口更改父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-update", + "metadata": { + "sidebarTitle": "更新监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout production", + "note": "Production services", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderUpdateRequest" + }, + "example": { + "id": 12, + "name": "Checkout production", + "note": "Production services", + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/move": { + "post": { + "operationId": "monit-folder-write-move", + "summary": "移动监控目录", + "description": "将监控目录及其后代移至其他目录下或顶层。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 您必须有权访问源目录和目标目录。\n- 必须传入 `dest_parent_id`;移至顶层时显式传 0。不能移至自身或后代目录下。\n- 后代目录路径在同一事务中更新。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-move", + "metadata": { + "sidebarTitle": "移动监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "空对象。" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderMoveRequest" + }, + "example": { + "id": 12, + "dest_parent_id": 0 + } + } + } + } + } + }, + "/monit/folder/delete": { + "post": { + "operationId": "monit-folder-write-delete", + "summary": "删除监控目录", + "description": "移除子目录、规则和仪表盘后,删除有权访问的监控目录。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 只能删除空目录。存在子目录、告警规则或仪表盘时,返回 HTTP 409,错误码为 `FolderNotEmpty`。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-delete", + "metadata": { + "sidebarTitle": "删除监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "空对象。" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + }, + "409": { + "description": "目录非空。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "description": "请求 ID。" + }, + "error": { + "type": "object", + "description": "错误详情。", + "properties": { + "code": { + "type": "string", + "description": "FolderNotEmpty:请移除目录内容后重试。", + "enum": [ + "FolderNotEmpty" + ] + }, + "message": { + "type": "string", + "description": "错误消息。" + } + }, + "required": [ + "code", + "message" + ] + } + }, + "required": [ + "request_id", + "error" + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "FolderNotEmpty", + "message": "Folder contains dashboards" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderDeleteRequest" + }, + "example": { + "id": 12 + } + } + } + } + } } }, "security": [ diff --git a/roundtrip_gen_test.go b/roundtrip_gen_test.go index 2c4cdc4..ea4cdb8 100644 --- a/roundtrip_gen_test.go +++ b/roundtrip_gen_test.go @@ -133,7 +133,10 @@ var exampleDataDecoders = map[string]func(json.RawMessage) error{ "POST /monit/datasource/sls/projects": func(d json.RawMessage) error { var v SLSProjectsResponse; return json.Unmarshal(d, &v) }, "POST /monit/datasource/tools/invoke": func(d json.RawMessage) error { var v DatasourceToolResult; return json.Unmarshal(d, &v) }, "POST /monit/datasource/update": func(d json.RawMessage) error { var v DataSourceItem; return json.Unmarshal(d, &v) }, + "POST /monit/folder/create": func(d json.RawMessage) error { var v FolderItem; return json.Unmarshal(d, &v) }, + "POST /monit/folder/health": func(d json.RawMessage) error { var v FolderHealthOutput; return json.Unmarshal(d, &v) }, "POST /monit/folder/list": func(d json.RawMessage) error { var v []FolderItem; return json.Unmarshal(d, &v) }, + "POST /monit/folder/update": func(d json.RawMessage) error { var v FolderItem; return json.Unmarshal(d, &v) }, "POST /monit/query/data": func(d json.RawMessage) error { var v QueryDataResponse; return json.Unmarshal(d, &v) }, "POST /monit/query/explore": func(d json.RawMessage) error { var v ExploreData; return json.Unmarshal(d, &v) }, "POST /monit/rule/audit/detail": func(d json.RawMessage) error { var v AlertRuleAudit; return json.Unmarshal(d, &v) }, diff --git a/rule_folders.go b/rule_folders.go index 7e1e85e..8baeecc 100644 --- a/rule_folders.go +++ b/rule_folders.go @@ -7,12 +7,26 @@ import "context" // RuleFoldersService handles the "Monitors/Rule folders" API resource. type RuleFoldersService service +// Get monitor folder health. +// +// Get health summaries for visible folders, including issues in their descendants. +// +// API: POST /monit/folder/health (monit-folder-read-health). +func (s *RuleFoldersService) ReadHealth(ctx context.Context) (*FolderHealthOutput, *Response, error) { + out := new(FolderHealthOutput) + resp, err := s.client.do(ctx, "/monit/folder/health", nil, out) + if err != nil { + return nil, resp, err + } + return out, resp, nil +} + // List monitor folders. // // List every monitor folder the caller can read. // // API: POST /monit/folder/list (monit-folder-read-list). -func (s *RuleFoldersService) List(ctx context.Context) (*[]FolderItem, *Response, error) { +func (s *RuleFoldersService) ReadList(ctx context.Context) (*[]FolderItem, *Response, error) { out := new([]FolderItem) resp, err := s.client.do(ctx, "/monit/folder/list", nil, out) if err != nil { @@ -20,3 +34,49 @@ func (s *RuleFoldersService) List(ctx context.Context) (*[]FolderItem, *Response } return out, resp, nil } + +// Create monitor folder. +// +// Create a monitor folder at the root or under an accessible parent folder. +// +// API: POST /monit/folder/create (monit-folder-write-create). +func (s *RuleFoldersService) WriteCreate(ctx context.Context, req *FolderCreateRequest) (*FolderItem, *Response, error) { + out := new(FolderItem) + resp, err := s.client.do(ctx, "/monit/folder/create", req, out) + if err != nil { + return nil, resp, err + } + return out, resp, nil +} + +// Delete monitor folder. +// +// Delete an accessible monitor folder after removing its children, rules, and dashboards. +// +// API: POST /monit/folder/delete (monit-folder-write-delete). +func (s *RuleFoldersService) WriteDelete(ctx context.Context, req *FolderDeleteRequest) (*Response, error) { + return s.client.do(ctx, "/monit/folder/delete", req, nil) +} + +// Move monitor folder. +// +// Move a monitor folder and its descendants under another folder or to the root. +// +// API: POST /monit/folder/move (monit-folder-write-move). +func (s *RuleFoldersService) WriteMove(ctx context.Context, req *FolderMoveRequest) (*Response, error) { + return s.client.do(ctx, "/monit/folder/move", req, nil) +} + +// Update monitor folder. +// +// Replace the name, note, and team assignment of an accessible monitor folder. +// +// API: POST /monit/folder/update (monit-folder-write-update). +func (s *RuleFoldersService) WriteUpdate(ctx context.Context, req *FolderUpdateRequest) (*FolderItem, *Response, error) { + out := new(FolderItem) + resp, err := s.client.do(ctx, "/monit/folder/update", req, out) + if err != nil { + return nil, resp, err + } + return out, resp, nil +} diff --git a/rule_folders_compat.go b/rule_folders_compat.go new file mode 100644 index 0000000..7af79b7 --- /dev/null +++ b/rule_folders_compat.go @@ -0,0 +1,8 @@ +package flashduty + +import "context" + +// List lists monitor folders. It preserves the original SDK method name. +func (s *RuleFoldersService) List(ctx context.Context) (*[]FolderItem, *Response, error) { + return s.ReadList(ctx) +} diff --git a/rule_folders_test.go b/rule_folders_test.go new file mode 100644 index 0000000..bc21394 --- /dev/null +++ b/rule_folders_test.go @@ -0,0 +1,77 @@ +package flashduty + +import ( + "context" + "encoding/json" + "net/http" + "reflect" + "testing" +) + +func TestRuleFoldersAppKeyRequests(t *testing.T) { + for _, action := range []string{"list", "health", "create", "update", "move", "delete"} { + t.Run(action, func(t *testing.T) { + client := newTestClient(t, func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost || r.URL.Path != "/monit/folder/"+action || r.URL.Query().Get("app_key") != "KEY" { + t.Errorf("unexpected request: %s %s", r.Method, r.URL.Redacted()) + } + if action == "move" { + var body map[string]any + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + t.Fatal(err) + } + // Moving to root requires an explicit zero on the wire. + if !reflect.DeepEqual(body, map[string]any{"id": float64(12), "dest_parent_id": float64(0)}) { + t.Errorf("move body = %#v", body) + } + } + w.Header().Set("Content-Type", "application/json") + data := any(map[string]any{}) + switch action { + case "list": + data = []map[string]any{{"id": 12, "name": "Checkout"}} + case "health": + data = map[string]any{"items": []map[string]any{{"folder_id": 12, "has_issue": true}}} + case "create", "update": + data = map[string]any{"id": 12, "name": "Checkout"} + } + _ = json.NewEncoder(w).Encode(map[string]any{"data": data}) + }) + ctx := context.Background() + var err error + switch action { + case "list": + out, _, e := client.RuleFolders.List(ctx) + err = e + if e == nil && (len(*out) != 1 || (*out)[0].ID != 12) { + t.Errorf("list = %#v", out) + } + case "health": + out, _, e := client.RuleFolders.ReadHealth(ctx) + err = e + if e == nil && (len(out.Items) != 1 || out.Items[0].FolderID != 12 || !out.Items[0].HasIssue) { + t.Errorf("health = %#v", out) + } + case "create": + out, _, e := client.RuleFolders.WriteCreate(ctx, &FolderCreateRequest{Name: "Checkout"}) + err = e + if e == nil && out.ID != 12 { + t.Errorf("create = %#v", out) + } + case "update": + out, _, e := client.RuleFolders.WriteUpdate(ctx, &FolderUpdateRequest{ID: 12, Name: "Checkout"}) + err = e + if e == nil && out.ID != 12 { + t.Errorf("update = %#v", out) + } + case "move": + _, err = client.RuleFolders.WriteMove(ctx, &FolderMoveRequest{ID: 12, DestParentID: 0}) + case "delete": + _, err = client.RuleFolders.WriteDelete(ctx, &FolderDeleteRequest{ID: 12}) + } + if err != nil { + t.Fatal(err) + } + }) + } +}