From dce7c882be68c9d1190e744edf9f1966c0ff7cb7 Mon Sep 17 00:00:00 2001 From: flashduty-bot Date: Thu, 10 Sep 2026 09:49:22 +0800 Subject: [PATCH 1/2] feat(monit): add datasource query tool params and sync invoke contract --- data_sources.go | 2 +- datasource_query_params.go | 169 ++++++++++++++ datasource_query_params_test.go | 99 ++++++++ internal/cmd/gen/main.go | 18 ++ models_gen.go | 4 +- openapi/openapi.en.json | 397 ++++++++++++++++++++++++++++++-- openapi/openapi.zh.json | 397 ++++++++++++++++++++++++++++++-- 7 files changed, 1043 insertions(+), 43 deletions(-) create mode 100644 datasource_query_params.go create mode 100644 datasource_query_params_test.go diff --git a/data_sources.go b/data_sources.go index d870ec0..dc51bbe 100644 --- a/data_sources.go +++ b/data_sources.go @@ -65,7 +65,7 @@ func (s *DataSourcesService) ReadSLSProjects(ctx context.Context, req *SLSProjec // Invoke datasource tool. // -// Execute one deterministic tool against a configured datasource. Requires all currently online routable Edge sessions in the cluster to support the v0.71.0 base invoke protocol; individual tools may require a newer implementation. No tool catalog, automatic replay, or fallback to Agent/legacy diagnose. Request body limit 128 KiB; complete success response limit 1 MiB; tool timeout at most 25 seconds. +// Execute one deterministic diagnostic or query tool against a configured datasource. // // API: POST /monit/datasource/tools/invoke (monit-datasource-tools-invoke). func (s *DataSourcesService) ToolsInvoke(ctx context.Context, req *DatasourceToolInvokeRequest) (*DatasourceToolResult, *Response, error) { diff --git a/datasource_query_params.go b/datasource_query_params.go new file mode 100644 index 0000000..cb7b22c --- /dev/null +++ b/datasource_query_params.go @@ -0,0 +1,169 @@ +package flashduty + +import ( + "encoding/json" + "errors" + "fmt" +) + +// Request-side params types for the `.query` datasource tools invoked +// through DataSourcesService.ToolsInvoke. +// +// These schemas are hand-written (not generated): they are not reachable from +// any requestBody in the OpenAPI spec — the invoke request carries `params` as +// raw JSON — so the generator would emit them as response-side types, where +// epoch-millis fields marshal to RFC3339 strings and optional fields lose +// `,omitempty`. Here, optional fields are pointers: a nil pointer omits the +// key so the server keeps its default, while a non-nil pointer sends the value +// explicitly (explicit null is rejected by the server, so there is no way to +// send one). Use the Bool/Int64/String helpers in ptr.go to set them. + +// DatasourceQueryExecution describes when a `.query` tool evaluates. +// +// Kind selects the evaluation mode: +// - `instant` evaluates once at ToMS; only ToMS is set. +// - `range` evaluates a stepped series over [FromMS, ToMS]; MaxDataPoints is +// required and bounds the returned points (the server computes the step). +// - `window` evaluates one bounded window [FromMS, ToMS]. +// +// The reserved `step_seconds` field is not modeled and must stay omitted. +type DatasourceQueryExecution struct { + Kind string `json:"kind"` + // Window or range start as a Unix epoch timestamp in milliseconds. + FromMS *int64 `json:"from_ms,omitempty"` + // Query end time as a Unix epoch timestamp in milliseconds; for `instant` + // it is the evaluation timestamp. + ToMS *int64 `json:"to_ms,omitempty"` + // `range` only, required: maximum returned data points. + MaxDataPoints *int64 `json:"max_data_points,omitempty"` + // `range` only, optional: positive lower bound in seconds for the computed + // step. + MinStepSeconds *int64 `json:"min_step_seconds,omitempty"` +} + +// PrometheusQueryParams are the params for `prometheus.query`. Expr is PromQL. +// Execution.Kind must be `instant` or `range`; `instant` accepts only ToMS. +type PrometheusQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` +} + +// MySQLQueryParams are the params for `mysql.query`. Expr is a single +// read-only SQL statement. Execution.Kind must be `window`. +type MySQLQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` +} + +// PostgresQueryParams are the params for `postgres.query`. Expr is a single +// read-only SQL statement. Execution.Kind must be `window`. +type PostgresQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` +} + +// OracleQueryParams are the params for `oracle.query`. Expr is a single +// read-only SQL statement. Execution.Kind must be `window`. +type OracleQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` +} + +// ClickHouseQueryParams are the params for `clickhouse.query`. Expr is a +// single read-only SQL statement. Execution.Kind must be `window`. +type ClickHouseQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` +} + +// ElasticsearchQueryParams are the params for `elasticsearch.query`. Expr is +// a single SQL statement; Elasticsearch DSL queries are not supported. +// Execution.Kind must be `window`. +type ElasticsearchQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` +} + +// LokiQueryParams are the params for `loki.query`. Expr is LogQL. +// Execution.Kind must be `instant` or `range`; `instant` keeps the full time +// context so `$__auto` ranges resolve. Limit and Direction only apply to +// raw-log results. +type LokiQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` + // Maximum raw-log entries to return (1–1000); only bounds raw logs, not + // SQL rows or scanned data. + Limit *int64 `json:"limit,omitempty"` + // Raw-log retrieval order: `latest` or `earliest`. + Direction *string `json:"direction,omitempty"` +} + +// VictoriaLogsQueryParams are the params for `victorialogs.query`. Expr is +// LogsQL. Use a `window` execution for raw logs (Limit/Direction allowed) or +// an `instant`/`range` execution with FromMS for stats queries +// (Limit/Direction rejected). +type VictoriaLogsQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` + // Maximum raw-log entries to return (1–1000); only bounds raw logs, not + // SQL rows or scanned data. + Limit *int64 `json:"limit,omitempty"` + // Raw-log retrieval order: `latest` or `earliest`. + Direction *string `json:"direction,omitempty"` +} + +// SLSQueryParams are the params for `sls.query` on Alibaba Cloud SLS. +// Execution.Kind must be `window`. +type SLSQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` + // SLS project name. + Project string `json:"project"` + // SLS logstore name. + Logstore string `json:"logstore"` + // Whether to run the query with SLS PowerSQL. A pointer because explicit + // false is a real value distinct from the executor default. + PowerSQL *bool `json:"powersql,omitempty"` + // Maximum raw-log entries to return (1–100); only bounds raw logs, not + // SQL rows or scanned data. + Limit *int64 `json:"limit,omitempty"` + // Raw-log retrieval order: `latest` or `earliest`. + Direction *string `json:"direction,omitempty"` +} + +// TencentCLSQueryParams are the params for `tencent_cls.query` on Tencent +// Cloud CLS. Execution.Kind must be `window`. +type TencentCLSQueryParams struct { + Expr string `json:"expr"` + Execution DatasourceQueryExecution `json:"execution"` + // Tencent Cloud region, e.g. `ap-guangzhou`. + Region string `json:"region"` + // CLS log topic ID. + TopicID string `json:"topic_id"` + // Search syntax: `cql` or `lucene`. + Syntax string `json:"syntax"` + // Maximum raw-log entries to return (1–1000); only bounds raw logs, not + // SQL rows or scanned data. + Limit *int64 `json:"limit,omitempty"` + // Raw-log retrieval order: `latest` or `earliest`. + Direction *string `json:"direction,omitempty"` +} + +// NewDatasourceQueryInvokeRequest builds a ToolsInvoke request for a +// `.query` tool by marshaling params (one of the *QueryParams types +// above) into the request's raw JSON Params. A nil params returns an error: +// query tools must not omit their params. +func NewDatasourceQueryInvokeRequest(datasourceID uint64, tool string, params any) (*DatasourceToolInvokeRequest, error) { + if params == nil { + return nil, errors.New("flashduty: query tool params must not be nil") + } + raw, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("flashduty: marshal query tool params: %w", err) + } + return &DatasourceToolInvokeRequest{ + DatasourceID: datasourceID, + Tool: tool, + Params: json.RawMessage(raw), + }, nil +} diff --git a/datasource_query_params_test.go b/datasource_query_params_test.go new file mode 100644 index 0000000..9e00de0 --- /dev/null +++ b/datasource_query_params_test.go @@ -0,0 +1,99 @@ +package flashduty + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "testing" +) + +func TestDatasourceQueryInvokeRequestSLSWireFormat(t *testing.T) { + // Beyond 2^53: must survive the wire as exact integers. + from := int64(9007199254740993) + to := int64(9007199254740994) + params := &SLSQueryParams{ + Expr: "status: 500", + Project: "my-project", + Logstore: "my-logstore", + PowerSQL: Bool(false), + Limit: Int64(50), + Execution: DatasourceQueryExecution{ + Kind: "window", + FromMS: Int64(from), + ToMS: Int64(to), + }, + } + req, err := NewDatasourceQueryInvokeRequest(42, "sls.query", params) + if err != nil { + t.Fatal(err) + } + client := newTestClient(t, func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost || r.URL.Path != "/monit/datasource/tools/invoke" { + t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path) + } + body, _ := io.ReadAll(r.Body) + if !strings.Contains(string(body), `"powersql":false`) { + t.Errorf("powersql lost or stringified: %s", body) + } + if !strings.Contains(string(body), `"from_ms":9007199254740993`) || !strings.Contains(string(body), `"to_ms":9007199254740994`) { + t.Errorf("epoch millis lost precision or changed form: %s", body) + } + decoder := json.NewDecoder(strings.NewReader(string(body))) + decoder.UseNumber() + var input map[string]any + if err := decoder.Decode(&input); err != nil { + t.Fatal(err) + } + execution, ok := input["params"].(map[string]any)["execution"].(map[string]any) + if !ok { + t.Fatalf("execution missing: %s", body) + } + fromWire, ok := execution["from_ms"].(json.Number) + if !ok { + t.Fatalf("from_ms is not a JSON number: %s", body) + } + if fromWire.String() != "9007199254740993" { + t.Errorf("from_ms precision lost: %s", fromWire) + } + w.Header().Set("Content-Type", "application/json") + _, _ = fmt.Fprintf(w, `{"request_id":"trace-query","data":{"datasource_id":42,"tool":"sls.query","data":{"rows":[]}}}`) + }) + result, _, err := client.DataSources.ToolsInvoke(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if result.Tool != "sls.query" || string(result.Data) != `{"rows":[]}` { + t.Fatalf("response changed: %+v", result) + } +} + +func TestVictoriaLogsQueryParamsInstantStatsOmitsRawLogFields(t *testing.T) { + params := &VictoriaLogsQueryParams{ + Expr: `* | stats count() as total`, + Execution: DatasourceQueryExecution{ + Kind: "instant", + FromMS: Int64(1757433600000), + ToMS: Int64(1757520000000), + }, + } + raw, err := json.Marshal(params) + if err != nil { + t.Fatal(err) + } + if strings.Contains(string(raw), `"limit"`) || strings.Contains(string(raw), `"direction"`) { + t.Fatalf("instant stats params carry raw-log keys: %s", raw) + } + if !strings.Contains(string(raw), `"from_ms":1757433600000`) { + t.Fatalf("from_ms missing or not numeric: %s", raw) + } +} + +func TestNewDatasourceQueryInvokeRequestRejectsNilParams(t *testing.T) { + req, err := NewDatasourceQueryInvokeRequest(42, "sls.query", nil) + if err == nil || req != nil { + t.Fatalf("nil params must fail: req=%+v err=%v", req, err) + } +} diff --git a/internal/cmd/gen/main.go b/internal/cmd/gen/main.go index de606aa..1c0d96b 100644 --- a/internal/cmd/gen/main.go +++ b/internal/cmd/gen/main.go @@ -99,6 +99,24 @@ func run() error { "DutyError": true, "AutomationRuleUpdateRequest": true, // hand-written to preserve partial-update pointer semantics. "SkillUploadRequest": true, // multipart form schema; the hand-written WriteUpload carries the file as an io.Reader. + // The .query tool params are request-side schemas, but they are + // not reachable from any requestBody (the invoke request carries + // params as x-flashduty-raw-json), so the generator would emit them + // as response-side types: epoch-millis fields would become + // TimestampMilli (marshaling to RFC3339 strings) and optional fields + // would lose omitempty. They are hand-written in + // datasource_query_params.go with request-side pointer semantics. + "DatasourceQueryExecution": true, + "PrometheusQueryParams": true, + "MySQLQueryParams": true, + "PostgresQueryParams": true, + "OracleQueryParams": true, + "ClickHouseQueryParams": true, + "ElasticsearchQueryParams": true, + "LokiQueryParams": true, + "VictoriaLogsQueryParams": true, + "SLSQueryParams": true, + "TencentCLSQueryParams": true, }, queued: map[string]bool{}, synth: map[string]any{}, diff --git a/models_gen.go b/models_gen.go index 82ab044..900864b 100644 --- a/models_gen.go +++ b/models_gen.go @@ -2795,9 +2795,9 @@ type DatasourceToolInvokeRequest struct { AccountID uint64 `json:"account_id,omitempty" toon:"account_id,omitempty"` // Datasource ID from /monit/datasource/list. DatasourceID uint64 `json:"datasource_id" toon:"datasource_id"` - // Tool-specific JSON parameters; omitted means {}. Explicit null is invalid. + // Tool-specific JSON parameters; omitted means {}. Explicit null is invalid. Query tools (`.query`) use the per-datasource params schemas named in the `tool` description. Params json.RawMessage `json:"params,omitempty" toon:"params,omitempty"` - // Single tool name prefixed by the datasource type, e.g. mysql.overview. Free SQL uses /monit/query/data; mysql.query and postgres.query are unsupported. + // Single tool name prefixed by the datasource type. Diagnostic tools are defined by the executing Edge (e.g. `mysql.overview`). Query tools are `.query` where `` is one of `prometheus`, `mysql`, `postgres`, `oracle`, `clickhouse`, `elasticsearch`, `loki`, `victorialogs`, `sls`, `tencent_cls`; their `params` follow `PrometheusQueryParams`, `MySQLQueryParams`, `PostgresQueryParams`, `OracleQueryParams`, `ClickHouseQueryParams`, `ElasticsearchQueryParams`, `LokiQueryParams`, `VictoriaLogsQueryParams`, `SLSQueryParams`, or `TencentCLSQueryParams` respectively. Tool string `json:"tool" toon:"tool"` } diff --git a/openapi/openapi.en.json b/openapi/openapi.en.json index f49bc25..0fb1a11 100644 --- a/openapi/openapi.en.json +++ b/openapi/openapi.en.json @@ -7536,6 +7536,318 @@ ], "type": "object" }, + "DatasourceQueryExecution": { + "type": "object", + "additionalProperties": true, + "required": [ + "kind", + "to_ms" + ], + "properties": { + "kind": { + "type": "string", + "enum": [ + "instant", + "range", + "window" + ], + "description": "Execution mode: `instant` evaluates at a single timestamp, `range` evaluates a stepped series, and `window` evaluates one bounded time window." + }, + "from_ms": { + "type": "integer", + "format": "int64", + "description": "Window or range start as a Unix epoch timestamp in milliseconds. Required for `range` and `window`; optional for Loki and VictoriaLogs `instant`; rejected by Prometheus `instant`." + }, + "to_ms": { + "type": "integer", + "format": "int64", + "description": "Query end time as a Unix epoch timestamp in milliseconds; for `instant` it is the evaluation timestamp." + }, + "max_data_points": { + "type": "integer", + "format": "int64", + "description": "`range` only, required: maximum returned data points; the server computes the effective step from the window." + }, + "min_step_seconds": { + "type": "integer", + "format": "int64", + "description": "`range` only, optional: positive lower bound in seconds for the computed step." + } + } + }, + "PrometheusQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "prometheus.query parameters. `expr` is PromQL. `execution.kind` must be `instant` or `range`; `instant` accepts only `to_ms`." + }, + "MySQLQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "mysql.query parameters. `expr` is a single read-only SQL statement. `execution.kind` must be `window`." + }, + "PostgresQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "postgres.query parameters. `expr` is a single read-only SQL statement. `execution.kind` must be `window`." + }, + "OracleQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "oracle.query parameters. `expr` is a single read-only SQL statement. `execution.kind` must be `window`." + }, + "ClickHouseQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "clickhouse.query parameters. `expr` is a single read-only SQL statement. `execution.kind` must be `window`." + }, + "ElasticsearchQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "elasticsearch.query parameters. `expr` is a single SQL statement; Elasticsearch DSL queries are not supported. `execution.kind` must be `window`." + }, + "LokiQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 1000, + "description": "Maximum raw-log entries to return (1–1000). Only bounds raw logs, not SQL rows or scanned data; omitted keeps the executor default. Explicit null is invalid." + }, + "direction": { + "type": "string", + "enum": [ + "latest", + "earliest" + ], + "description": "Raw-log retrieval order: `latest` returns the newest entries first, `earliest` the oldest. Omitted keeps the executor default." + } + }, + "description": "loki.query parameters. `expr` is LogQL. `execution.kind` must be `instant` or `range`; `instant` keeps the full time context so `$__auto` ranges resolve. `limit` and `direction` only apply to raw-log results." + }, + "VictoriaLogsQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 1000, + "description": "Maximum raw-log entries to return (1–1000). Only bounds raw logs, not SQL rows or scanned data; omitted keeps the executor default. Explicit null is invalid." + }, + "direction": { + "type": "string", + "enum": [ + "latest", + "earliest" + ], + "description": "Raw-log retrieval order: `latest` returns the newest entries first, `earliest` the oldest. Omitted keeps the executor default." + } + }, + "description": "victorialogs.query parameters. `expr` is LogsQL. Use `window` for raw logs (`limit`/`direction` allowed) or `instant`/`range` with `from_ms` for stats queries (`limit`/`direction` rejected)." + }, + "SLSQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution", + "project", + "logstore" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum raw-log entries to return (1–100). Only bounds raw logs, not SQL rows or scanned data; omitted keeps the executor default. Explicit null is invalid." + }, + "direction": { + "type": "string", + "enum": [ + "latest", + "earliest" + ], + "description": "Raw-log retrieval order: `latest` returns the newest entries first, `earliest` the oldest. Omitted keeps the executor default." + }, + "project": { + "type": "string", + "minLength": 1, + "description": "SLS project name." + }, + "logstore": { + "type": "string", + "minLength": 1, + "description": "SLS logstore name." + }, + "powersql": { + "type": "boolean", + "description": "Whether to run the query with SLS PowerSQL. Omitted keeps the executor default; explicit null is invalid." + } + }, + "description": "sls.query parameters for Alibaba Cloud SLS. `execution.kind` must be `window`." + }, + "TencentCLSQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution", + "region", + "topic_id", + "syntax" + ], + "properties": { + "expr": { + "type": "string", + "description": "Query expression evaluated by the datasource; dialect depends on the datasource type." + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 1000, + "description": "Maximum raw-log entries to return (1–1000). Only bounds raw logs, not SQL rows or scanned data; omitted keeps the executor default. Explicit null is invalid." + }, + "direction": { + "type": "string", + "enum": [ + "latest", + "earliest" + ], + "description": "Raw-log retrieval order: `latest` returns the newest entries first, `earliest` the oldest. Omitted keeps the executor default." + }, + "region": { + "type": "string", + "minLength": 1, + "description": "Tencent Cloud region, e.g. `ap-guangzhou`." + }, + "topic_id": { + "type": "string", + "minLength": 1, + "description": "CLS log topic ID." + }, + "syntax": { + "type": "string", + "enum": [ + "cql", + "lucene" + ], + "description": "Search syntax: `cql` or `lucene`." + } + }, + "description": "tencent_cls.query parameters for Tencent Cloud CLS. `execution.kind` must be `window`." + }, "DatasourceToolInvokeRequest": { "properties": { "account_id": { @@ -7551,12 +7863,12 @@ }, "params": { "additionalProperties": true, - "description": "Tool-specific JSON parameters; omitted means {}. Explicit null is invalid.", + "description": "Tool-specific JSON parameters; omitted means {}. Explicit null is invalid. Query tools (`.query`) use the per-datasource params schemas named in the `tool` description.", "type": "object", "x-flashduty-raw-json": true }, "tool": { - "description": "Single tool name prefixed by the datasource type, e.g. mysql.overview. Free SQL uses /monit/query/data; mysql.query and postgres.query are unsupported.", + "description": "Single tool name prefixed by the datasource type. Diagnostic tools are defined by the executing Edge (e.g. `mysql.overview`). Query tools are `.query` where `` is one of `prometheus`, `mysql`, `postgres`, `oracle`, `clickhouse`, `elasticsearch`, `loki`, `victorialogs`, `sls`, `tencent_cls`; their `params` follow `PrometheusQueryParams`, `MySQLQueryParams`, `PostgresQueryParams`, `OracleQueryParams`, `ClickHouseQueryParams`, `ElasticsearchQueryParams`, `LokiQueryParams`, `VictoriaLogsQueryParams`, `SLSQueryParams`, or `TencentCLSQueryParams` respectively.", "maxLength": 128, "minLength": 1, "type": "string" @@ -42073,18 +42385,35 @@ }, "/monit/datasource/tools/invoke": { "post": { - "description": "Execute one deterministic tool against a configured datasource. Requires all currently online routable Edge sessions in the cluster to support the v0.71.0 base invoke protocol; individual tools may require a newer implementation. No tool catalog, automatic replay, or fallback to Agent/legacy diagnose. Request body limit 128 KiB; complete success response limit 1 MiB; tool timeout at most 25 seconds.", + "description": "Execute one deterministic diagnostic or query tool against a configured datasource.", "operationId": "monit-datasource-tools-invoke", "requestBody": { "content": { "application/json": { - "example": { - "datasource_id": 10, - "params": {}, - "tool": "mysql.overview" - }, "schema": { "$ref": "#/components/schemas/DatasourceToolInvokeRequest" + }, + "examples": { + "diagnostic": { + "value": { + "datasource_id": 10, + "params": {}, + "tool": "mysql.overview" + } + }, + "query": { + "value": { + "datasource_id": 24000, + "tool": "prometheus.query", + "params": { + "expr": "sum(rate(http_requests_total[5m]))", + "execution": { + "kind": "instant", + "to_ms": 1789000000000 + } + } + } + } } } }, @@ -42094,17 +42423,6 @@ "200": { "content": { "application/json": { - "example": { - "data": { - "data": { - "version": "8.0.36" - }, - "datasource_id": 10, - "summary": "MySQL overview", - "tool": "mysql.overview" - }, - "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" - }, "schema": { "allOf": [ { @@ -42119,6 +42437,45 @@ "type": "object" } ] + }, + "examples": { + "diagnostic": { + "value": { + "data": { + "data": { + "version": "8.0.36" + }, + "datasource_id": 10, + "summary": "MySQL overview", + "tool": "mysql.overview" + }, + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" + } + }, + "query": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "datasource_id": 24000, + "tool": "prometheus.query", + "data": { + "format": "explore_result.v1", + "result": { + "kind": "samples", + "samples": [ + { + "labels": { + "__name__": "up", + "instance": "10.101.214.50:7070" + }, + "value": 1 + } + ] + } + } + } + } + } } } }, @@ -42233,7 +42590,7 @@ "Monitors/Data sources" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **2,000 requests/minute**; **32 requests/second** per account |\n| Permissions | **Datasources Read** (`monit`) |\n\nUse datasource IDs from `/monit/datasource/list`. Disabled datasources return `datasource_disabled`; `alerting_enabled=false` does not block tools. Errors use non-2xx HTTP status and `error.code`, `error.message`, `error.reason`. `tool_not_supported` indicates the selected executor does not provide this tool; it is not a vendor permission error. Never retry through another Edge or the legacy diagnose endpoint automatically.", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **2,000 requests/minute**; **32 requests/second** per account |\n| Permissions | **Datasources Read** (`monit`) |\n\nUse datasource IDs from `/monit/datasource/list`. Disabled datasources return `datasource_disabled`; `alerting_enabled=false` does not block tools. Errors use non-2xx HTTP status and `error.code`, `error.message`, `error.reason`. `tool_not_supported` indicates the selected executor does not provide this tool; it is not a vendor permission error. Never retry through another Edge or the legacy diagnose endpoint automatically.\n\n## Usage\n\n- Two tool families share this entry: diagnostic tools defined by the executing Edge (e.g. `mysql.overview`, `prometheus.metric_trends`) and query tools named `.query`. The tool prefix must match the datasource type.\n- Query tools require the Edge cluster to support Explore queries (protocol milestone v0.68.0); diagnostic tools require the v0.71.0 base invoke protocol. Unsupported clusters fail with `edge_upgrade_required`, `mixed_edge_versions`, or `edge_version_unknown`; never fall back to `/monit/query/data` or another endpoint automatically.\n- For query tools, `params` follows the per-datasource schema named in the `tool` field description. `expr` and `execution` are always required. `limit`/`direction` only bound raw-log retrieval, never SQL rows or scanned data. Unknown extension fields are tolerated but never executed or forwarded.\n- Query `data` is the complete Explore result: `format` is `explore_result.v1` and `result.kind` is `samples`, `frames`, or `logs`; log results keep `applied_limit` and `has_more`. Query results never synthesize `summary` or `truncated`.\n- Request body limit 128 KiB; complete success response limit 10 MiB for both families; diagnostic tool timeout at most 25 seconds.", "href": "/en/api-reference/monitors/data-sources/monit-datasource-tools-invoke", "metadata": { "sidebarTitle": "Invoke datasource tool" diff --git a/openapi/openapi.zh.json b/openapi/openapi.zh.json index 338f132..10e38aa 100644 --- a/openapi/openapi.zh.json +++ b/openapi/openapi.zh.json @@ -7536,6 +7536,318 @@ ], "type": "object" }, + "DatasourceQueryExecution": { + "type": "object", + "additionalProperties": true, + "required": [ + "kind", + "to_ms" + ], + "properties": { + "kind": { + "type": "string", + "enum": [ + "instant", + "range", + "window" + ], + "description": "执行模式:`instant` 在单个时间点求值,`range` 按步长求值时间序列,`window` 在单个有界时间窗口内求值。" + }, + "from_ms": { + "type": "integer", + "format": "int64", + "description": "窗口或范围的起始时间,Unix 毫秒时间戳。`range` 和 `window` 必填;Loki 和 VictoriaLogs 的 `instant` 可选;Prometheus 的 `instant` 拒绝该字段。" + }, + "to_ms": { + "type": "integer", + "format": "int64", + "description": "查询截止时间,Unix 毫秒时间戳;`instant` 模式下为求值时间点。" + }, + "max_data_points": { + "type": "integer", + "format": "int64", + "description": "仅 `range` 且必填:最大返回点数;服务端按时间窗口计算实际步长。" + }, + "min_step_seconds": { + "type": "integer", + "format": "int64", + "description": "仅 `range`,可选:计算步长的正数下限(秒)。" + } + } + }, + "PrometheusQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "prometheus.query 参数。`expr` 为 PromQL。`execution.kind` 取 `instant` 或 `range`;`instant` 只接受 `to_ms`。" + }, + "MySQLQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "mysql.query 参数。`expr` 为单条只读 SQL。`execution.kind` 必须为 `window`。" + }, + "PostgresQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "postgres.query 参数。`expr` 为单条只读 SQL。`execution.kind` 必须为 `window`。" + }, + "OracleQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "oracle.query 参数。`expr` 为单条只读 SQL。`execution.kind` 必须为 `window`。" + }, + "ClickHouseQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "clickhouse.query 参数。`expr` 为单条只读 SQL。`execution.kind` 必须为 `window`。" + }, + "ElasticsearchQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + } + }, + "description": "elasticsearch.query 参数。`expr` 为单条 SQL 语句;不支持 Elasticsearch DSL 查询。`execution.kind` 必须为 `window`。" + }, + "LokiQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 1000, + "description": "返回的原始日志最大条数(1–1000)。只约束原始日志,不代表 SQL 行数或扫描量;省略时沿用执行端默认值。显式 null 非法。" + }, + "direction": { + "type": "string", + "enum": [ + "latest", + "earliest" + ], + "description": "原始日志检索方向:`latest` 先返回最新条目,`earliest` 先返回最旧条目。省略时沿用执行端默认值。" + } + }, + "description": "loki.query 参数。`expr` 为 LogQL。`execution.kind` 取 `instant` 或 `range`;`instant` 保留完整时间上下文以便 `$__auto` 范围解析。`limit` 和 `direction` 只作用于原始日志结果。" + }, + "VictoriaLogsQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 1000, + "description": "返回的原始日志最大条数(1–1000)。只约束原始日志,不代表 SQL 行数或扫描量;省略时沿用执行端默认值。显式 null 非法。" + }, + "direction": { + "type": "string", + "enum": [ + "latest", + "earliest" + ], + "description": "原始日志检索方向:`latest` 先返回最新条目,`earliest` 先返回最旧条目。省略时沿用执行端默认值。" + } + }, + "description": "victorialogs.query 参数。`expr` 为 LogsQL。`window` 用于原始日志(允许 `limit`/`direction`);`instant`/`range` 统计查询必须带 `from_ms`(拒绝 `limit`/`direction`)。" + }, + "SLSQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution", + "project", + "logstore" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "返回的原始日志最大条数(1–100)。只约束原始日志,不代表 SQL 行数或扫描量;省略时沿用执行端默认值。显式 null 非法。" + }, + "direction": { + "type": "string", + "enum": [ + "latest", + "earliest" + ], + "description": "原始日志检索方向:`latest` 先返回最新条目,`earliest` 先返回最旧条目。省略时沿用执行端默认值。" + }, + "project": { + "type": "string", + "minLength": 1, + "description": "SLS Project 名称。" + }, + "logstore": { + "type": "string", + "minLength": 1, + "description": "SLS Logstore 名称。" + }, + "powersql": { + "type": "boolean", + "description": "是否以 SLS PowerSQL 执行查询。省略时沿用执行端默认值;显式 null 非法。" + } + }, + "description": "阿里云 SLS 的 sls.query 参数。`execution.kind` 必须为 `window`。" + }, + "TencentCLSQueryParams": { + "type": "object", + "additionalProperties": true, + "required": [ + "expr", + "execution", + "region", + "topic_id", + "syntax" + ], + "properties": { + "expr": { + "type": "string", + "description": "由数据源求值的查询表达式;方言取决于数据源类型。" + }, + "execution": { + "$ref": "#/components/schemas/DatasourceQueryExecution" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 1000, + "description": "返回的原始日志最大条数(1–1000)。只约束原始日志,不代表 SQL 行数或扫描量;省略时沿用执行端默认值。显式 null 非法。" + }, + "direction": { + "type": "string", + "enum": [ + "latest", + "earliest" + ], + "description": "原始日志检索方向:`latest` 先返回最新条目,`earliest` 先返回最旧条目。省略时沿用执行端默认值。" + }, + "region": { + "type": "string", + "minLength": 1, + "description": "腾讯云地域,如 `ap-guangzhou`。" + }, + "topic_id": { + "type": "string", + "minLength": 1, + "description": "CLS 日志主题 ID。" + }, + "syntax": { + "type": "string", + "enum": [ + "cql", + "lucene" + ], + "description": "检索语法:`cql` 或 `lucene`。" + } + }, + "description": "腾讯云 CLS 的 tencent_cls.query 参数。`execution.kind` 必须为 `window`。" + }, "DatasourceToolInvokeRequest": { "properties": { "account_id": { @@ -7551,12 +7863,12 @@ }, "params": { "additionalProperties": true, - "description": "工具专属 JSON 参数,省略时为 {},显式 null 非法。", + "description": "工具专属 JSON 参数;省略等同于 {}。显式 null 非法。查询工具(`.query`)使用 `tool` 描述中按数据源列出的专属参数 Schema。", "type": "object", "x-flashduty-raw-json": true }, "tool": { - "description": "以数据源类型为前缀的单个工具名,如 mysql.overview。自由 SQL 使用 /monit/query/data;不支持 mysql.query 和 postgres.query。", + "description": "以数据源类型为前缀的单个工具名。诊断工具由执行端 Edge 定义(如 `mysql.overview`)。查询工具为 `.query`,`` 取 `prometheus`、`mysql`、`postgres`、`oracle`、`clickhouse`、`elasticsearch`、`loki`、`victorialogs`、`sls`、`tencent_cls` 之一;`params` 分别遵循 `PrometheusQueryParams`、`MySQLQueryParams`、`PostgresQueryParams`、`OracleQueryParams`、`ClickHouseQueryParams`、`ElasticsearchQueryParams`、`LokiQueryParams`、`VictoriaLogsQueryParams`、`SLSQueryParams`、`TencentCLSQueryParams`。", "maxLength": 128, "minLength": 1, "type": "string" @@ -42073,18 +42385,35 @@ }, "/monit/datasource/tools/invoke": { "post": { - "description": "对已配置的数据源执行单个确定性工具。要求集群所有当前在线可路由 Edge 会话支持 v0.71.0 基础 invoke 协议;具体工具可能需要更新实现。不提供工具目录、自动重放或 Agent/旧 diagnose 回退。请求体上限 128 KiB,完整成功响应上限 1 MiB,工具超时最多 25 秒。", + "description": "对已配置的数据源执行单个确定性诊断或查询工具。", "operationId": "monit-datasource-tools-invoke", "requestBody": { "content": { "application/json": { - "example": { - "datasource_id": 10, - "params": {}, - "tool": "mysql.overview" - }, "schema": { "$ref": "#/components/schemas/DatasourceToolInvokeRequest" + }, + "examples": { + "diagnostic": { + "value": { + "datasource_id": 10, + "params": {}, + "tool": "mysql.overview" + } + }, + "query": { + "value": { + "datasource_id": 24000, + "tool": "prometheus.query", + "params": { + "expr": "sum(rate(http_requests_total[5m]))", + "execution": { + "kind": "instant", + "to_ms": 1789000000000 + } + } + } + } } } }, @@ -42094,17 +42423,6 @@ "200": { "content": { "application/json": { - "example": { - "data": { - "data": { - "version": "8.0.36" - }, - "datasource_id": 10, - "summary": "MySQL overview", - "tool": "mysql.overview" - }, - "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" - }, "schema": { "allOf": [ { @@ -42119,6 +42437,45 @@ "type": "object" } ] + }, + "examples": { + "diagnostic": { + "value": { + "data": { + "data": { + "version": "8.0.36" + }, + "datasource_id": 10, + "summary": "MySQL overview", + "tool": "mysql.overview" + }, + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" + } + }, + "query": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "datasource_id": 24000, + "tool": "prometheus.query", + "data": { + "format": "explore_result.v1", + "result": { + "kind": "samples", + "samples": [ + { + "labels": { + "__name__": "up", + "instance": "10.101.214.50:7070" + }, + "value": 1 + } + ] + } + } + } + } + } } } }, @@ -42233,7 +42590,7 @@ "Monitors/告警数据源" ], "x-mint": { - "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **2,000 次/分钟**;**32 次/秒** |\n| 权限要求 | **数据源查看**(`monit`) |\n\n通过 `/monit/datasource/list` 获取数据源 ID。停用数据源返回 `datasource_disabled`,`alerting_enabled=false` 不阻断工具。错误使用非 2xx HTTP 状态和 `error.code`、`error.message`、`error.reason`。`tool_not_supported` 表示选中的执行端未提供该工具,不表示厂商权限不足。禁止自动切换 Edge 或回退旧 diagnose 重试。", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **2,000 次/分钟**;**32 次/秒** |\n| 权限要求 | **数据源查看**(`monit`) |\n\n通过 `/monit/datasource/list` 获取数据源 ID。停用数据源返回 `datasource_disabled`,`alerting_enabled=false` 不阻断工具。错误使用非 2xx HTTP 状态和 `error.code`、`error.message`、`error.reason`。`tool_not_supported` 表示选中的执行端未提供该工具,不表示厂商权限不足。禁止自动切换 Edge 或回退旧 diagnose 重试。\n\n## 使用说明\n\n- 本入口共用两类工具:由执行端 Edge 定义的诊断工具(如 `mysql.overview`、`prometheus.metric_trends`)和命名为 `.query` 的查询工具。工具前缀必须与数据源类型一致。\n- 查询工具要求 Edge 集群支持 Explore 查询(协议里程碑 v0.68.0);诊断工具要求 v0.71.0 基础 invoke 协议。不支持的集群返回 `edge_upgrade_required`、`mixed_edge_versions` 或 `edge_version_unknown`;禁止自动回退 `/monit/query/data` 或其他接口。\n- 查询工具的 `params` 遵循 `tool` 字段描述中按数据源列出的专属 Schema。`expr` 和 `execution` 必填。`limit`/`direction` 只约束原始日志条数与检索方向,不代表 SQL 行数或扫描量。未知扩展字段被容忍,但不参与执行也不透传。\n- 查询结果的 `data` 是完整 Explore 结果:`format` 为 `explore_result.v1`,`result.kind` 为 `samples`、`frames` 或 `logs`;日志结果保留 `applied_limit` 和 `has_more`。查询结果不合成 `summary` 或 `truncated`。\n- 请求体上限 128 KiB;两类工具的完整成功响应上限均为 10 MiB;诊断工具超时最多 25 秒。", "href": "/zh/api-reference/monitors/data-sources/monit-datasource-tools-invoke", "metadata": { "sidebarTitle": "调用数据源工具" From d0537f407324ef1b586d7535b4f40bd30579cca6 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Wed, 9 Sep 2026 20:48:40 -0700 Subject: [PATCH 2/2] chore: sync the corrected from_ms note from the docs spec --- openapi/openapi.en.json | 2 +- openapi/openapi.zh.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openapi/openapi.en.json b/openapi/openapi.en.json index 0fb1a11..fde38c6 100644 --- a/openapi/openapi.en.json +++ b/openapi/openapi.en.json @@ -7556,7 +7556,7 @@ "from_ms": { "type": "integer", "format": "int64", - "description": "Window or range start as a Unix epoch timestamp in milliseconds. Required for `range` and `window`; optional for Loki and VictoriaLogs `instant`; rejected by Prometheus `instant`." + "description": "Window or range start as a Unix epoch timestamp in milliseconds. Required for `range`, `window`, and every VictoriaLogs execution; optional for Loki `instant`; rejected by Prometheus `instant`." }, "to_ms": { "type": "integer", diff --git a/openapi/openapi.zh.json b/openapi/openapi.zh.json index 10e38aa..90bc053 100644 --- a/openapi/openapi.zh.json +++ b/openapi/openapi.zh.json @@ -7556,7 +7556,7 @@ "from_ms": { "type": "integer", "format": "int64", - "description": "窗口或范围的起始时间,Unix 毫秒时间戳。`range` 和 `window` 必填;Loki 和 VictoriaLogs 的 `instant` 可选;Prometheus 的 `instant` 拒绝该字段。" + "description": "窗口或范围的起始时间,Unix 毫秒时间戳。`range` 和 `window` 必填,VictoriaLogs 的任何执行类型都必填;Loki 的 `instant` 可选;Prometheus 的 `instant` 拒绝该字段。" }, "to_ms": { "type": "integer",