diff --git a/crates/clickhouse-cloud-api/README.md b/crates/clickhouse-cloud-api/README.md index c8503d73d..9232acb4a 100644 --- a/crates/clickhouse-cloud-api/README.md +++ b/crates/clickhouse-cloud-api/README.md @@ -107,6 +107,12 @@ The spec no longer accepts `sandboxVersion` when creating a UDF or a UDF version `ReversePrivateEndpoint` adds read-only `dns_targets` (`ReversePrivateEndpointDnsTarget`: `id`, `kind`, `internal_dns_name`) and `private_dns_mappings` (`ReversePrivateEndpointPrivateDnsMapping`: `private_dns_name`, `internal_dns_name`). `CustomPrivateDnsMapping` and `CustomPrivateDnsMappingResponse` gain optional `target_id`, which routes a VPC_RESOURCE custom DNS name to one of the endpoint's `dns_targets`; leave it `None` to keep the default target. Struct-literal callers supply the new fields as `None` or use `..Default::default()`. +The GCP `PgSize` values now carry local-SSD names, matching the spec: `c4-`, `c4a-` and `c4d-` sizes add `-lssd` (`c4-highmem-16` becomes `c4-highmem-lssd-16`, variant `C4_highmem_lssd_16`), and Z3 sizes add `-highmem` (`z3-highlssd-8` becomes `z3-highmem-highlssd-8`, variant `Z3_highmem_highlssd_8`). This is a breaking change for Rust callers: rename the variant. A response carrying a retired name deserializes to `PgSize::Unknown` rather than failing. + +`PgConfig` and `PgConfigResponse` gain background-writer (`bgwriter_delay`, `bgwriter_flush_after`, `bgwriter_lru_maxpages`, `bgwriter_lru_multiplier`), I/O (`io_method`, `io_workers`, `maintenance_io_concurrency`), memory (`hash_mem_multiplier`, `logical_decoding_work_mem`, `temp_buffers`), logging (`log_lock_waits`, `log_min_duration_statement`, `log_parameter_max_length`, `log_temp_files`), replication (`max_logical_replication_workers`, `max_replication_slots`, `max_sync_workers_per_subscription`, `max_wal_senders`) and other settings (`default_toast_compression`, `enable_parallel_hash`, `track_activity_query_size`, `vacuum_cost_delay`). The four closed string settings use `PgConfigDefaultToastCompression`, `PgConfigEnableParallelHash`, `PgConfigIoMethod` and `PgConfigLogLockWaits`, each with a `VALUES` const. They join the existing partial-schema optionality exemptions and the `From` write-back. Struct-literal callers need the new fields as `None` or `..Default::default()`. + +Postgres services gain an IPv4 access list. `PostgresService.ip_access_list` returns the current rules as `PostgresIpAccessListEntryResponse` (`id`, `source`, `port_range`, `description`); IPv6 rules are not included. `PostgresServicePostRequest.ip_access_list` sets the rules at creation: `None` allows every IPv4 and IPv6 address on 5432 and 6432, `Some(vec![])` blocks public access, and a list allows only those rules, so include one rule per required port. `PostgresServicePatchRequest.ip_access_list` takes `PostgresIpAccessListPatch`, which removes rules by ID (`PostgresIpAccessListRuleReference`) and then adds new rules or edits existing ones (include the returned `id`). Updates can partially succeed, so read the current list before retrying. `TryFrom` turns a fetched rule into an edit. Struct-literal callers of the Postgres create and patch requests need `ip_access_list: None` or `..Default::default()`. + ### ClickHouse settings models `ServiceClickhouseSettingsPatchRequest` uses a map of setting names to JSON values, and `ServiceClickhouseSettingsPatchResponse.settings` returns the applied map. The published OpenAPI now describes both fields as nonempty objects with string or integer values. Use `ServiceClickhouseSettingsPatchRequest` and `ServiceClickhouseSettingValue` for typed requests; the default JSON-value map remains source-compatible with existing callers. Explicit `ServiceClickhouseSettingsPatchRequest` callers remain supported: encoded objects are validated and serialized as objects before sending. diff --git a/crates/clickhouse-cloud-api/clickhouse_cloud_openapi.json b/crates/clickhouse-cloud-api/clickhouse_cloud_openapi.json index 0b6faf1a1..98dadc6c1 100644 --- a/crates/clickhouse-cloud-api/clickhouse_cloud_openapi.json +++ b/crates/clickhouse-cloud-api/clickhouse_cloud_openapi.json @@ -5,7 +5,7 @@ "version": "1.0", "contact": { "name": "ClickHouse Support", - "url": "https://clickhouse.com/docs/en/cloud/manage/openapi?referrer=openapi-1256081", + "url": "https://clickhouse.com/docs/en/cloud/manage/openapi?referrer=openapi-1310615", "email": "support@clickhouse.com" } }, @@ -13186,11 +13186,11 @@ ] } }, - "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/webhooks/{clickStackWebhookId}": { - "put": { - "summary": "ClickStack: Update Webhook", - "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Replaces an existing webhook. Readable optional fields (`description`, `body`) are a full replace: omitting them clears them. The write-only fields `headers` and `queryParams` are never returned on read, so omitting them preserves the stored values; send an explicit empty object (`{}`) to clear them. Exception: if the destination (`url` or `service`) changes, omitted `headers`/ `queryParams` are cleared rather than preserved so stored secrets are never forwarded to a new destination.", - "operationId": "clickStackUpdateWebhook", + "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/webhooks/validate": { + "post": { + "summary": "ClickStack: Validate Webhook", + "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Validates a webhook body against the same schema, URL rules, and body template check used by POST /api/v2/webhooks. The webhook is **never persisted**. Use this endpoint at plan time (e.g. from a Terraform provider) to check that a webhook configuration is valid before applying it. Name uniqueness is not checked.", + "operationId": "clickStackValidateWebhook", "parameters": [ { "in": "path", @@ -13211,15 +13211,6 @@ "type": "string", "format": "uuid" } - }, - { - "in": "path", - "name": "clickStackWebhookId", - "description": "Webhook ID", - "required": true, - "schema": { - "type": "string" - } } ], "requestBody": { @@ -13250,7 +13241,7 @@ "format": "uuid" }, "result": { - "$ref": "#/components/schemas/ClickStackWebhook" + "$ref": "#/components/schemas/ClickStackValidateWebhookResponse" } } } @@ -13326,11 +13317,13 @@ "position": "after" } ] - }, - "delete": { - "summary": "ClickStack: Delete Webhook", - "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Deletes a webhook. Blocked with a 409 while any alert still references it — reassign or remove those alerts first — so deletion never leaves an alert pointing at a missing webhook (which would silently drop notifications). Mirrors the internal webhook delete guard.", - "operationId": "clickStackDeleteWebhook", + } + }, + "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/webhooks/{clickStackWebhookId}": { + "put": { + "summary": "ClickStack: Update Webhook", + "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Replaces an existing webhook. Readable optional fields (`description`, `body`) are a full replace: omitting them clears them. The write-only fields `headers` and `queryParams` are never returned on read, so omitting them preserves the stored values; send an explicit empty object (`{}`) to clear them. Exception: if the destination (`url` or `service`) changes, omitted `headers`/ `queryParams` are cleared rather than preserved so stored secrets are never forwarded to a new destination.", + "operationId": "clickStackUpdateWebhook", "parameters": [ { "in": "path", @@ -13362,127 +13355,15 @@ } } ], - "responses": { - "200": { - "description": "Successful response", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "status": { - "type": "number", - "description": "HTTP status code.", - "example": 200 - }, - "requestId": { - "type": "string", - "description": "Unique id assigned to every request. UUIDv4", - "format": "uuid" - } - } - } - } - } - }, - "400": { - "description": "The request cannot be processed due to a client error. Please verify your request parameters and try again.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "status": { - "type": "number", - "description": "HTTP status code.", - "example": 400 - }, - "error": { - "type": "string", - "description": "Detailed error description." - }, - "requestId": { - "type": "string", - "description": "Unique id assigned to every request. UUIDv4", - "format": "uuid" - } - } - } - } - } - }, - "500": { - "description": "An internal server error has occurred. If this issue persists, please contact ClickHouse Cloud support for assistance.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "status": { - "type": "integer", - "description": "HTTP status code.", - "example": 500 - }, - "error": { - "type": "string", - "description": "Detailed error description." - }, - "requestId": { - "type": "string", - "description": "Unique id assigned to every request. UUIDv4", - "format": "uuid" - } - } - } + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ClickStackWebhookInput" } } } }, - "security": [ - { - "basicAuth": [ - "control-plane:service:manage-clickstack-api" - ] - } - ], - "tags": [ - "ClickStack" - ], - "x-badges": [ - { - "name": "Beta", - "position": "after" - } - ] - } - }, - "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/roles": { - "get": { - "summary": "ClickStack: List Roles", - "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Retrieves all roles for the authenticated team, including predefined roles.", - "operationId": "clickStackListRoles", - "parameters": [ - { - "in": "path", - "name": "organizationId", - "description": "ID of the organization that owns the service.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "in": "path", - "name": "serviceId", - "description": "ID of the ClickStack service.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], "responses": { "200": { "description": "Successful response", @@ -13502,10 +13383,7 @@ "format": "uuid" }, "result": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ClickStackRole" - } + "$ref": "#/components/schemas/ClickStackWebhook" } } } @@ -13582,10 +13460,10 @@ } ] }, - "post": { - "summary": "ClickStack: Create Role", - "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Creates a new custom role for the team.", - "operationId": "clickStackCreateRole", + "delete": { + "summary": "ClickStack: Delete Webhook", + "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Deletes a webhook. Blocked with a 409 while any alert still references it — reassign or remove those alerts first — so deletion never leaves an alert pointing at a missing webhook (which would silently drop notifications). Mirrors the internal webhook delete guard.", + "operationId": "clickStackDeleteWebhook", "parameters": [ { "in": "path", @@ -13606,17 +13484,17 @@ "type": "string", "format": "uuid" } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ClickStackCreateRoleRequest" - } + }, + { + "in": "path", + "name": "clickStackWebhookId", + "description": "Webhook ID", + "required": true, + "schema": { + "type": "string" } } - }, + ], "responses": { "200": { "description": "Successful response", @@ -13634,9 +13512,6 @@ "type": "string", "description": "Unique id assigned to every request. UUIDv4", "format": "uuid" - }, - "result": { - "$ref": "#/components/schemas/ClickStackRole" } } } @@ -13714,11 +13589,11 @@ ] } }, - "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/roles/{clickStackRoleId}": { + "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/roles": { "get": { - "summary": "ClickStack: Get Role", - "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Retrieves a specific role by ID.", - "operationId": "clickStackGetRole", + "summary": "ClickStack: List Roles", + "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Retrieves all roles for the authenticated team, including predefined roles.", + "operationId": "clickStackListRoles", "parameters": [ { "in": "path", @@ -13739,15 +13614,6 @@ "type": "string", "format": "uuid" } - }, - { - "in": "path", - "name": "clickStackRoleId", - "description": "id parameter", - "required": true, - "schema": { - "type": "string" - } } ], "responses": { @@ -13769,7 +13635,10 @@ "format": "uuid" }, "result": { - "$ref": "#/components/schemas/ClickStackRole" + "type": "array", + "items": { + "$ref": "#/components/schemas/ClickStackRole" + } } } } @@ -13846,10 +13715,10 @@ } ] }, - "put": { - "summary": "ClickStack: Update Role", - "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Updates a custom role's permissions, name, and description. Predefined roles cannot be modified.", - "operationId": "clickStackUpdateRole", + "post": { + "summary": "ClickStack: Create Role", + "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Creates a new custom role for the team.", + "operationId": "clickStackCreateRole", "parameters": [ { "in": "path", @@ -13870,22 +13739,13 @@ "type": "string", "format": "uuid" } - }, - { - "in": "path", - "name": "clickStackRoleId", - "description": "id parameter", - "required": true, - "schema": { - "type": "string" - } } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ClickStackUpdateRoleRequest" + "$ref": "#/components/schemas/ClickStackCreateRoleRequest" } } } @@ -13985,11 +13845,13 @@ "position": "after" } ] - }, - "delete": { - "summary": "ClickStack: Delete Role", - "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Deletes a custom role. Predefined roles, the team default user role, and roles assigned to users cannot be deleted.", - "operationId": "clickStackDeleteRole", + } + }, + "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/roles/{clickStackRoleId}": { + "get": { + "summary": "ClickStack: Get Role", + "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Retrieves a specific role by ID.", + "operationId": "clickStackGetRole", "parameters": [ { "in": "path", @@ -14038,6 +13900,9 @@ "type": "string", "description": "Unique id assigned to every request. UUIDv4", "format": "uuid" + }, + "result": { + "$ref": "#/components/schemas/ClickStackRole" } } } @@ -14113,13 +13978,11 @@ "position": "after" } ] - } - }, - "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/saved-searches": { - "get": { - "summary": "ClickStack: List Saved Searches", - "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Retrieves saved searches for the authenticated team (paginated). Results are capped at `limit` (default and maximum 1000). When `totalCount` exceeds the number of returned items, page with `limit`/`offset` to retrieve them all.", - "operationId": "clickStackListSavedSearches", + }, + "put": { + "summary": "ClickStack: Update Role", + "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Updates a custom role's permissions, name, and description. Predefined roles cannot be modified.", + "operationId": "clickStackUpdateRole", "parameters": [ { "in": "path", @@ -14142,27 +14005,24 @@ } }, { - "in": "query", - "name": "limit", - "description": "Maximum number of results to return.", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 1000, - "default": 1000 - } - }, - { - "in": "query", - "name": "offset", - "description": "Number of results to skip before returning.", + "in": "path", + "name": "clickStackRoleId", + "description": "id parameter", + "required": true, "schema": { - "type": "integer", - "minimum": 0, - "default": 0 + "type": "string" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ClickStackUpdateRoleRequest" + } + } + } + }, "responses": { "200": { "description": "Successful response", @@ -14182,10 +14042,283 @@ "format": "uuid" }, "result": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ClickStackSavedSearch" - } + "$ref": "#/components/schemas/ClickStackRole" + } + } + } + } + } + }, + "400": { + "description": "The request cannot be processed due to a client error. Please verify your request parameters and try again.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "number", + "description": "HTTP status code.", + "example": 400 + }, + "error": { + "type": "string", + "description": "Detailed error description." + }, + "requestId": { + "type": "string", + "description": "Unique id assigned to every request. UUIDv4", + "format": "uuid" + } + } + } + } + } + }, + "500": { + "description": "An internal server error has occurred. If this issue persists, please contact ClickHouse Cloud support for assistance.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "integer", + "description": "HTTP status code.", + "example": 500 + }, + "error": { + "type": "string", + "description": "Detailed error description." + }, + "requestId": { + "type": "string", + "description": "Unique id assigned to every request. UUIDv4", + "format": "uuid" + } + } + } + } + } + } + }, + "security": [ + { + "basicAuth": [ + "control-plane:service:manage-clickstack-api" + ] + } + ], + "tags": [ + "ClickStack" + ], + "x-badges": [ + { + "name": "Beta", + "position": "after" + } + ] + }, + "delete": { + "summary": "ClickStack: Delete Role", + "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Deletes a custom role. Predefined roles, the team default user role, and roles assigned to users cannot be deleted.", + "operationId": "clickStackDeleteRole", + "parameters": [ + { + "in": "path", + "name": "organizationId", + "description": "ID of the organization that owns the service.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "path", + "name": "serviceId", + "description": "ID of the ClickStack service.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "path", + "name": "clickStackRoleId", + "description": "id parameter", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "number", + "description": "HTTP status code.", + "example": 200 + }, + "requestId": { + "type": "string", + "description": "Unique id assigned to every request. UUIDv4", + "format": "uuid" + } + } + } + } + } + }, + "400": { + "description": "The request cannot be processed due to a client error. Please verify your request parameters and try again.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "number", + "description": "HTTP status code.", + "example": 400 + }, + "error": { + "type": "string", + "description": "Detailed error description." + }, + "requestId": { + "type": "string", + "description": "Unique id assigned to every request. UUIDv4", + "format": "uuid" + } + } + } + } + } + }, + "500": { + "description": "An internal server error has occurred. If this issue persists, please contact ClickHouse Cloud support for assistance.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "integer", + "description": "HTTP status code.", + "example": 500 + }, + "error": { + "type": "string", + "description": "Detailed error description." + }, + "requestId": { + "type": "string", + "description": "Unique id assigned to every request. UUIDv4", + "format": "uuid" + } + } + } + } + } + } + }, + "security": [ + { + "basicAuth": [ + "control-plane:service:manage-clickstack-api" + ] + } + ], + "tags": [ + "ClickStack" + ], + "x-badges": [ + { + "name": "Beta", + "position": "after" + } + ] + } + }, + "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/saved-searches": { + "get": { + "summary": "ClickStack: List Saved Searches", + "description": "**This endpoint is in beta.** API contract is stable, and no breaking changes are expected in the future.

ClickStack: Retrieves saved searches for the authenticated team (paginated). Results are capped at `limit` (default and maximum 1000). When `totalCount` exceeds the number of returned items, page with `limit`/`offset` to retrieve them all.", + "operationId": "clickStackListSavedSearches", + "parameters": [ + { + "in": "path", + "name": "organizationId", + "description": "ID of the organization that owns the service.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "path", + "name": "serviceId", + "description": "ID of the ClickStack service.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "limit", + "description": "Maximum number of results to return.", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 1000, + "default": 1000 + } + }, + { + "in": "query", + "name": "offset", + "description": "Number of results to skip before returning.", + "schema": { + "type": "integer", + "minimum": 0, + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "number", + "description": "HTTP status code.", + "example": 200 + }, + "requestId": { + "type": "string", + "description": "Unique id assigned to every request. UUIDv4", + "format": "uuid" + }, + "result": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ClickStackSavedSearch" + } } } } @@ -28493,7 +28626,7 @@ "example": "production_db" }, "authentication": { - "description": "Authentication method for Postgres connection.", + "description": "Authentication method for Postgres connection. IAM_ROLE is only available for services on AWS.", "type": "string", "enum": [ "basic", @@ -28503,7 +28636,7 @@ "example": "IAM_ROLE" }, "iamRole": { - "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication).", + "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication, services on AWS only).", "type": "string", "example": "arn:aws:iam::123456789012:role/MyApplicationRole" }, @@ -28585,7 +28718,7 @@ "$ref": "#/components/schemas/ClickPipePostgresPipeSettings" }, "authentication": { - "description": "Authentication method for Postgres connection.", + "description": "Authentication method for Postgres connection. IAM_ROLE is only available for services on AWS.", "type": "string", "enum": [ "basic", @@ -28595,7 +28728,7 @@ "example": "IAM_ROLE" }, "iamRole": { - "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication).", + "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication, services on AWS only).", "type": "string", "example": "arn:aws:iam::123456789012:role/MyApplicationRole" }, @@ -28979,7 +29112,7 @@ "example": 3306 }, "authentication": { - "description": "Authentication method for MySQL connection.", + "description": "Authentication method for MySQL connection. IAM_ROLE is only available for services on AWS.", "type": "string", "enum": [ "basic", @@ -28989,7 +29122,7 @@ "example": "basic" }, "iamRole": { - "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication).", + "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication, services on AWS only).", "type": "string", "example": "arn:aws:iam::123456789012:role/MyApplicationRole" }, @@ -29074,7 +29207,7 @@ "$ref": "#/components/schemas/ClickPipeMySQLPipeSettings" }, "authentication": { - "description": "Authentication method for MySQL connection.", + "description": "Authentication method for MySQL connection. IAM_ROLE is only available for services on AWS.", "type": "string", "enum": [ "basic", @@ -29084,7 +29217,7 @@ "example": "basic" }, "iamRole": { - "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication).", + "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication, services on AWS only).", "type": "string", "example": "arn:aws:iam::123456789012:role/MyApplicationRole" }, @@ -29155,7 +29288,7 @@ "example": 3306 }, "authentication": { - "description": "Authentication method for MySQL connection.", + "description": "Authentication method for MySQL connection. IAM_ROLE is only available for services on AWS.", "type": [ "string", "null" @@ -29168,7 +29301,7 @@ "example": "basic" }, "iamRole": { - "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication).", + "description": "IAM role ARN for IAM authentication (required for IAM_ROLE authentication, services on AWS only).", "type": [ "string", "null" @@ -29278,6 +29411,11 @@ "minimum": 1, "example": 86400 }, + "syncCron": { + "description": "Standard 5-field cron expression (or a descriptor such as \"@hourly\") that schedules CDC syncs, e.g. \"0 * * * *\" to sync every hour sharp. Evaluated in UTC unless prefixed with \"CRON_TZ=\". Must not fire more than once a minute. Syncs run on this schedule instead of every syncIntervalSeconds, so the two cannot both be set. Not allowed for \"snapshot\" replication mode.", + "type": "string", + "example": "0 * * * *" + }, "allowNullableColumns": { "description": "Allow nullable columns in the destination table.", "type": "boolean" @@ -31803,303 +31941,1476 @@ } } }, - "ClickStackAlertExecutionError": { + "ClickStackAlertChartConfigOverlay": { "properties": { - "timestamp": { - "description": "When the error occurred.", + "name": { + "description": "Display name for the alert query. Used in notification titles and as an alert-name fallback when the alert itself has no name.", "type": "string", - "format": "date-time", - "example": "2026-04-17T12:00:00.000Z" + "example": "Error Rate Query" }, - "type": { - "description": "Category of the error.", + "where": { + "description": "Chart-level filter applied on top of every select item's own \"where\" (combined via AND). Builder variants only; rejected on Raw SQL variants (filter inside the sqlTemplate instead).", "type": "string", - "enum": [ - "QUERY_ERROR", - "QUERY_TIMEOUT", - "WEBHOOK_ERROR", - "INVALID_ALERT", - "UNKNOWN" - ], - "example": "QUERY_ERROR" + "example": "ServiceName:api" }, - "message": { - "description": "Human-readable error message.", + "whereLanguage": { + "description": "Language of the chart-level \"where\" filter.", "type": "string", - "example": "Query timed out after 30s" + "enum": [ + "sql", + "lucene" + ] } - }, - "required": [ - "timestamp", - "type", - "message" - ] + } }, - "ClickStackAlertSilenced": { + "ClickStackNumberFormat": { "properties": { - "by": { - "description": "User ID who silenced the alert.", - "type": [ - "string", - "null" + "output": { + "description": "Output format applied to the number.", + "type": "string", + "enum": [ + "currency", + "percent", + "byte", + "time", + "number", + "data_rate", + "throughput", + "duration" ], - "example": "65f5e4a3b9e77c001a234567" + "example": "number" }, - "at": { - "description": "Silence start timestamp.", + "mantissa": { + "description": "Number of decimal places.", + "type": "integer", + "example": 2 + }, + "thousandSeparated": { + "description": "Whether to use thousand separators.", + "type": "boolean", + "example": true + }, + "average": { + "description": "Whether to show as average.", + "type": "boolean", + "example": false + }, + "decimalBytes": { + "description": "Use decimal bytes (1000) vs binary bytes (1024).", + "type": "boolean", + "example": false + }, + "factor": { + "description": "Multiplication factor.", + "type": "number", + "example": 1 + }, + "currencySymbol": { + "description": "Currency symbol for currency format.", "type": "string", - "format": "date-time", - "example": "2026-03-19T08:00:00.000Z" + "example": "$" }, - "until": { - "description": "Silence end timestamp.", + "numericUnit": { + "description": "Numeric unit for data, data rate, or throughput formats.", "type": "string", - "format": "date-time", - "example": "2026-03-20T08:00:00.000Z" + "enum": [ + "bytes_iec", + "bytes_si", + "bits_iec", + "bits_si", + "kibibytes", + "kilobytes", + "mebibytes", + "megabytes", + "gibibytes", + "gigabytes", + "tebibytes", + "terabytes", + "pebibytes", + "petabytes", + "packets_sec", + "bytes_sec_iec", + "bytes_sec_si", + "bits_sec_iec", + "bits_sec_si", + "kibibytes_sec", + "kibibits_sec", + "kilobytes_sec", + "kilobits_sec", + "mebibytes_sec", + "mebibits_sec", + "megabytes_sec", + "megabits_sec", + "gibibytes_sec", + "gibibits_sec", + "gigabytes_sec", + "gigabits_sec", + "tebibytes_sec", + "tebibits_sec", + "terabytes_sec", + "terabits_sec", + "pebibytes_sec", + "pebibits_sec", + "petabytes_sec", + "petabits_sec", + "cps", + "ops", + "rps", + "reads_sec", + "wps", + "iops", + "cpm", + "opm", + "rpm_reads", + "wpm" + ], + "example": "bytes_iec" + }, + "unit": { + "description": "Custom unit label.", + "type": "string", + "example": "ms" } } }, - "ClickStackAlertChannelEmail": { + "ClickStackSelectItem": { "properties": { - "type": { - "description": "Channel type. Must be \"email\" for email alerts.", + "aggFn": { + "description": "Aggregation function to apply. \"count\" does not require a valueExpression; \"quantile\" requires a level field indicating the desired percentile (e.g., 0.95).", "type": "string", "enum": [ - "webhook", - "email" - ] + "avg", + "count", + "count_distinct", + "last_value", + "max", + "min", + "quantile", + "sum", + "any", + "none" + ], + "example": "count" }, - "emailRecipients": { - "type": "array", - "description": "Email recipients for email alerts.", - "items": { - "type": "string" - } - } - }, - "required": [ - "type", - "emailRecipients" - ] - }, - "ClickStackAlertChannelWebhook": { - "properties": { - "type": { - "description": "Channel type. Must be \"webhook\" for webhook alerts.", + "valueExpression": { + "description": "Expression for the column or value to aggregate. Must be omitted when aggFn is \"count\"; required for all other aggFn values.", + "type": "string", + "example": "Duration" + }, + "alias": { + "description": "Display alias for this select item in chart legends.", + "type": "string", + "example": "Request Duration" + }, + "level": { + "description": "Percentile level; only valid when aggFn is \"quantile\". Allowed values: 0.5, 0.9, 0.95, 0.99.", + "type": "number" + }, + "where": { + "description": "SQL or Lucene filter condition applied before aggregation.", + "type": "string", + "example": "service:api" + }, + "whereLanguage": { + "description": "Query language for the where clause.", "type": "string", "enum": [ - "webhook", - "email" + "sql", + "lucene" ] }, - "webhookId": { - "description": "Webhook destination ID.", + "metricName": { + "description": "Name of the metric to aggregate; only applicable when the source is a metrics source.", "type": "string", - "example": "65f5e4a3b9e77c001a789012" + "example": "http.server.duration" }, - "webhookService": { - "description": "Webhook service type (e.g., slack_api).", - "type": [ - "string", - "null" - ], - "example": "slack_api" + "metricType": { + "description": "Metric type; only applicable when the source is a metrics source.", + "type": "string", + "enum": [ + "sum", + "gauge", + "histogram", + "summary", + "exponential histogram" + ] }, - "slackChannelId": { - "description": "Slack channel ID for Slack webhooks.", - "type": [ - "string", - "null" + "periodAggFn": { + "description": "Optional period aggregation function for Gauge metrics (e.g., compute the delta over the period).", + "type": "string", + "enum": [ + "delta" ], - "example": "C01ABCDEF23" + "example": "delta" }, - "severity": { - "description": "Severity label used by PagerDuty API webhooks.", - "type": [ - "string", - "null" - ], - "enum": [ - "critical", - "error", - "warning", - "info" - ] + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" } }, "required": [ - "type", - "webhookId" + "aggFn" ] }, - "ClickStackAlertChannel": { - "oneOf": [ - { - "$ref": "#/components/schemas/ClickStackAlertChannelEmail" + "ClickStackFormula": { + "properties": { + "expression": { + "description": "Arithmetic expression over the select items by position, e.g. \"A / (A + B) * 100\" for a success-rate percentage.", + "type": "string", + "example": "A / (A + B) * 100" }, - { - "$ref": "#/components/schemas/ClickStackAlertChannelWebhook" + "alias": { + "description": "Display label for the formula series in chart legends and column headers. Falls back to the raw expression text when unset.", + "type": "string", + "example": "Success rate %" + }, + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" } + }, + "required": [ + "expression" ] }, - "ClickStackAlertChannels": { - "type": "array", - "description": "Notification channels to trigger when the alert fires or resolves. Between 1 and 10 channels; duplicates are rejected.", - "minItems": 1, - "maxItems": 10, - "items": { - "$ref": "#/components/schemas/ClickStackAlertChannel" - } - }, - "ClickStackAlertResponse": { + "ClickStackLineBuilderChartConfig": { "properties": { - "dashboardId": { - "description": "Dashboard ID for tile-based alerts.", - "type": [ - "string", - "null" + "displayType": { + "description": "Display type discriminator. Must be \"line\" for line charts.", + "type": "string", + "enum": [ + "line" ], - "example": "65f5e4a3b9e77c001a567890" + "example": "line" }, - "tileId": { - "description": "Tile ID for tile-based alerts. Must be a line, stacked bar, or number type tile.", - "type": [ - "string", - "null" - ], - "example": "65f5e4a3b9e77c001a901234" + "sourceId": { + "description": "ID of the data source to query.", + "type": "string", + "example": "65f5e4a3b9e77c001a111111" }, - "savedSearchId": { - "description": "Saved search ID for saved_search alerts.", - "type": [ - "string", - "null" - ], - "example": "65f5e4a3b9e77c001a345678" + "select": { + "type": "array", + "description": "One or more aggregated values to plot. When asRatio is true, exactly two select items are required.", + "items": { + "$ref": "#/components/schemas/ClickStackSelectItem" + } }, "groupBy": { - "description": "Group-by key for saved search alerts.", - "type": [ - "string", - "null" - ], - "example": "ServiceName" + "description": "Field expression to group results by (creates separate lines per group value).", + "type": "string", + "example": "host" }, - "threshold": { - "description": "Threshold value for triggering the alert. For between and not_between threshold types, this is the lower bound.", - "type": "number", - "example": 100 + "asRatio": { + "description": "Plot select[0] / select[1] as a ratio. Requires exactly two select items.", + "type": "boolean" }, - "thresholdMax": { - "description": "Upper bound for between and not_between threshold types. Required when thresholdType is between or not_between, must be >= threshold.", - "type": [ - "number", - "null" - ], - "example": 500 + "alignDateRangeToGranularity": { + "description": "Expand date range boundaries to the query granularity interval.", + "type": "boolean" }, - "interval": { - "description": "Evaluation interval for the alert. `30s` requires the 30s alert interval feature to be enabled for your team.", + "fillNulls": { + "description": "Fill missing time buckets with zero instead of leaving gaps.", + "type": "boolean" + }, + "fitYAxisToData": { + "description": "Set the y-axis lower bound to the minimum of the displayed data instead of zero, making small fluctuations between series easier to see.", + "type": "boolean" + }, + "lineInterpolation": { + "description": "How points are connected. Defaults to \"smooth\" when omitted.", "type": "string", "enum": [ - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "6h", - "12h", - "1d" + "smooth", + "linear", + "step" ], - "example": "1h" + "example": "step" }, - "scheduleOffsetMinutes": { - "description": "Offset from the interval boundary in minutes. For example, 2 with a 5m interval evaluates windows at :02, :07, :12, etc. (UTC).", - "type": [ - "integer", - "null" - ], - "example": 2 + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" }, - "scheduleStartAt": { - "description": "Absolute UTC start time anchor. Alert windows start from this timestamp and repeat every interval.", - "type": [ - "string", - "null" - ], - "format": "date-time", - "example": "2026-02-08T10:00:00.000Z" + "compareToPreviousPeriod": { + "description": "Overlay the equivalent previous time period for comparison.", + "type": "boolean" }, - "source": { - "description": "Alert source type (tile-based or saved search).", + "seriesLimit": { + "description": "Maximum number of series rendered (top-N by value). Omit to use the default render cap, set 0 for unlimited, or a positive N to keep the top N series.", + "type": "integer", + "example": 5 + }, + "formulas": { + "type": "array", + "description": "Derived series computed from the select items via letter-ref arithmetic (\"A\" = select[0], \"B\" = select[1], ...). Metric, log, and trace sources only. Cannot be combined with asRatio.", + "items": { + "$ref": "#/components/schemas/ClickStackFormula" + } + }, + "showOperandSeries": { + "description": "Only meaningful with formulas. When false, only the formula series are returned; the raw operand series are hidden.", + "type": "boolean" + } + }, + "required": [ + "displayType", + "sourceId", + "select" + ] + }, + "ClickStackLineRawSqlChartConfig": { + "properties": { + "configType": { + "description": "Must be \"sql\" to use the Raw SQL chart config variant.", "type": "string", "enum": [ - "saved_search", - "tile" + "sql" ], - "example": "tile" + "example": "sql" }, - "thresholdType": { - "description": "Threshold comparison direction.", + "connectionId": { + "description": "ID of the ClickHouse connection to execute the query against.", "type": "string", - "enum": [ - "above", - "below", - "above_exclusive", - "below_or_equal", - "equal", - "not_equal", - "between", - "not_between" - ], - "example": "above" + "example": "65f5e4a3b9e77c001a567890" }, - "channel": { - "$ref": "#/components/schemas/ClickStackAlertChannel" + "sqlTemplate": { + "description": "SQL query template to execute. Supports HyperDX template variables.", + "type": "string", + "example": "SELECT count() FROM otel_logs WHERE timestamp > now() - INTERVAL 1 HOUR" }, - "channels": { - "$ref": "#/components/schemas/ClickStackAlertChannels" + "sourceId": { + "description": "Optional ID of the data source associated with this Raw SQL chart. Used for applying dashboard filters.", + "type": "string", + "example": "65f5e4a3b9e77c001a567890" }, - "name": { - "description": "Human-friendly alert name.", - "type": [ - "string", - "null" - ], - "example": "Test Alert" + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" }, - "message": { - "description": "Alert message template.", - "type": [ - "string", - "null" + "displayType": { + "description": "Display as a line time-series chart.", + "type": "string", + "enum": [ + "line" ], - "example": "Test Alert Message" + "example": "line" }, - "note": { - "description": "Freeform note for the alert. Supports markdown formatting.", - "type": [ - "string", - "null" - ], - "example": "Threshold raised from 50 to 100 on 2026-01-15. See [runbook](https://wiki.example.com/runbook)." + "compareToPreviousPeriod": { + "description": "Overlay the equivalent previous time period for comparison.", + "type": "boolean" }, - "numConsecutiveWindows": { - "description": "Fire the alert only after its condition has been met for this many consecutive evaluation windows. While the condition is met but fewer than this many consecutive windows have violated, the alert is in the PENDING state.", - "type": [ - "integer", - "null" - ], - "example": 3 + "fillNulls": { + "description": "Fill missing time buckets with zero instead of leaving gaps.", + "type": "boolean" }, - "id": { - "description": "Unique alert identifier.", - "type": "string", - "example": "65f5e4a3b9e77c001a123456" + "alignDateRangeToGranularity": { + "description": "Expand date range boundaries to the query granularity interval.", + "type": "boolean" }, - "state": { - "description": "Current alert state.", + "fitYAxisToData": { + "description": "Set the y-axis lower bound to the minimum of the displayed data instead of zero, making small fluctuations between series easier to see.", + "type": "boolean" + }, + "lineInterpolation": { + "description": "How points are connected. Defaults to \"smooth\" when omitted.", + "type": "string", + "enum": [ + "smooth", + "linear", + "step" + ], + "example": "step" + } + }, + "required": [ + "configType", + "connectionId", + "sqlTemplate", + "displayType" + ] + }, + "ClickStackLineChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackLineBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackLineRawSqlChartConfig" + } + ] + }, + "ClickStackAlertLineBuilderChartConfig": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackLineBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertChartConfigOverlay" + } + ] + }, + "ClickStackAlertLineRawSqlChartConfig": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackLineRawSqlChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertChartConfigOverlay" + } + ] + }, + "ClickStackAlertLineChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackAlertLineBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertLineRawSqlChartConfig" + } + ] + }, + "ClickStackBarBuilderChartConfig": { + "properties": { + "displayType": { + "description": "Display type discriminator. Must be \"stacked_bar\" for stacked-bar charts.", + "type": "string", + "enum": [ + "stacked_bar" + ], + "example": "stacked_bar" + }, + "sourceId": { + "description": "ID of the data source to query.", + "type": "string", + "example": "65f5e4a3b9e77c001a111111" + }, + "select": { + "type": "array", + "description": "One or more aggregated values to plot. When asRatio is true, exactly two select items are required.", + "items": { + "$ref": "#/components/schemas/ClickStackSelectItem" + } + }, + "groupBy": { + "description": "Field expression to group results by (creates separate bars segments per group value).", + "type": "string", + "example": "service" + }, + "asRatio": { + "description": "Plot select[0] / select[1] as a ratio. Requires exactly two select items.", + "type": "boolean" + }, + "alignDateRangeToGranularity": { + "description": "Align the date range boundaries to the query granularity interval.", + "type": "boolean" + }, + "fillNulls": { + "description": "Fill missing time buckets with zero instead of leaving gaps.", + "type": "boolean" + }, + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" + }, + "seriesLimit": { + "description": "Maximum number of series rendered (top-N by value). Omit to use the default render cap, set 0 for unlimited, or a positive N to keep the top N series.", + "type": "integer", + "example": 5 + }, + "formulas": { + "type": "array", + "description": "Derived series computed from the select items via letter-ref arithmetic (\"A\" = select[0], \"B\" = select[1], ...). Metric, log, and trace sources only. Cannot be combined with asRatio.", + "items": { + "$ref": "#/components/schemas/ClickStackFormula" + } + }, + "showOperandSeries": { + "description": "Only meaningful with formulas. When false, only the formula series are returned; the raw operand series are hidden.", + "type": "boolean" + } + }, + "required": [ + "displayType", + "sourceId", + "select" + ] + }, + "ClickStackBarRawSqlChartConfig": { + "properties": { + "configType": { + "description": "Must be \"sql\" to use the Raw SQL chart config variant.", + "type": "string", + "enum": [ + "sql" + ], + "example": "sql" + }, + "connectionId": { + "description": "ID of the ClickHouse connection to execute the query against.", + "type": "string", + "example": "65f5e4a3b9e77c001a567890" + }, + "sqlTemplate": { + "description": "SQL query template to execute. Supports HyperDX template variables.", + "type": "string", + "example": "SELECT count() FROM otel_logs WHERE timestamp > now() - INTERVAL 1 HOUR" + }, + "sourceId": { + "description": "Optional ID of the data source associated with this Raw SQL chart. Used for applying dashboard filters.", + "type": "string", + "example": "65f5e4a3b9e77c001a567890" + }, + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" + }, + "displayType": { + "description": "Display as a stacked-bar time-series chart.", + "type": "string", + "enum": [ + "stacked_bar" + ], + "example": "stacked_bar" + }, + "fillNulls": { + "description": "Fill missing time buckets with zero instead of leaving gaps.", + "type": "boolean" + }, + "alignDateRangeToGranularity": { + "description": "Expand date range boundaries to the query granularity interval.", + "type": "boolean" + } + }, + "required": [ + "configType", + "connectionId", + "sqlTemplate", + "displayType" + ] + }, + "ClickStackBarChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackBarBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackBarRawSqlChartConfig" + } + ] + }, + "ClickStackAlertBarBuilderChartConfig": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackBarBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertChartConfigOverlay" + } + ] + }, + "ClickStackAlertBarRawSqlChartConfig": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackBarRawSqlChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertChartConfigOverlay" + } + ] + }, + "ClickStackAlertBarChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackAlertBarBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertBarRawSqlChartConfig" + } + ] + }, + "ClickStackStackedLineBuilderChartConfig": { + "properties": { + "displayType": { + "description": "Display type discriminator. Must be \"stacked_line\" for stacked-line charts.", + "type": "string", + "enum": [ + "stacked_line" + ], + "example": "stacked_line" + }, + "sourceId": { + "description": "ID of the data source to query.", + "type": "string", + "example": "65f5e4a3b9e77c001a111111" + }, + "select": { + "type": "array", + "description": "One or more aggregated values to plot. When asRatio is true, exactly two select items are required.", + "items": { + "$ref": "#/components/schemas/ClickStackSelectItem" + } + }, + "groupBy": { + "description": "Field expression to group results by (creates separate stacked bands per group value).", + "type": "string", + "example": "service" + }, + "asRatio": { + "description": "Plot select[0] / select[1] as a ratio. Requires exactly two select items.", + "type": "boolean" + }, + "alignDateRangeToGranularity": { + "description": "Align the date range boundaries to the query granularity interval.", + "type": "boolean" + }, + "fillNulls": { + "description": "Fill missing time buckets with zero instead of leaving gaps.", + "type": "boolean" + }, + "lineInterpolation": { + "description": "How points are connected. Defaults to \"linear\" when omitted.", + "type": "string", + "enum": [ + "smooth", + "linear", + "step" + ], + "example": "step" + }, + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" + }, + "seriesLimit": { + "description": "Maximum number of series rendered (top-N by value). Omit to use the default render cap, set 0 for unlimited, or a positive N to keep the top N series.", + "type": "integer", + "example": 5 + }, + "formulas": { + "type": "array", + "description": "Derived series computed from the select items via letter-ref arithmetic (\"A\" = select[0], \"B\" = select[1], ...). Metric, log, and trace sources only. Cannot be combined with asRatio.", + "items": { + "$ref": "#/components/schemas/ClickStackFormula" + } + }, + "showOperandSeries": { + "description": "Only meaningful with formulas. When false, only the formula series are returned; the raw operand series are hidden.", + "type": "boolean" + } + }, + "required": [ + "displayType", + "sourceId", + "select" + ] + }, + "ClickStackStackedLineRawSqlChartConfig": { + "properties": { + "configType": { + "description": "Must be \"sql\" to use the Raw SQL chart config variant.", + "type": "string", + "enum": [ + "sql" + ], + "example": "sql" + }, + "connectionId": { + "description": "ID of the ClickHouse connection to execute the query against.", + "type": "string", + "example": "65f5e4a3b9e77c001a567890" + }, + "sqlTemplate": { + "description": "SQL query template to execute. Supports HyperDX template variables.", + "type": "string", + "example": "SELECT count() FROM otel_logs WHERE timestamp > now() - INTERVAL 1 HOUR" + }, + "sourceId": { + "description": "Optional ID of the data source associated with this Raw SQL chart. Used for applying dashboard filters.", + "type": "string", + "example": "65f5e4a3b9e77c001a567890" + }, + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" + }, + "displayType": { + "description": "Display as a stacked-line time-series chart.", + "type": "string", + "enum": [ + "stacked_line" + ], + "example": "stacked_line" + }, + "fillNulls": { + "description": "Fill missing time buckets with zero instead of leaving gaps.", + "type": "boolean" + }, + "alignDateRangeToGranularity": { + "description": "Expand date range boundaries to the query granularity interval.", + "type": "boolean" + }, + "lineInterpolation": { + "description": "How points are connected. Defaults to \"linear\" when omitted.", + "type": "string", + "enum": [ + "smooth", + "linear", + "step" + ], + "example": "step" + } + }, + "required": [ + "configType", + "connectionId", + "sqlTemplate", + "displayType" + ] + }, + "ClickStackStackedLineChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackStackedLineBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackStackedLineRawSqlChartConfig" + } + ] + }, + "ClickStackAlertStackedLineBuilderChartConfig": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackStackedLineBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertChartConfigOverlay" + } + ] + }, + "ClickStackAlertStackedLineRawSqlChartConfig": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackStackedLineRawSqlChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertChartConfigOverlay" + } + ] + }, + "ClickStackAlertStackedLineChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackAlertStackedLineBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertStackedLineRawSqlChartConfig" + } + ] + }, + "ClickStackNumericColorCondition": { + "properties": { + "operator": { + "description": "Numeric comparison operator.", + "type": "string", + "enum": [ + "gt", + "gte", + "lt", + "lte" + ], + "example": "gt" + }, + "value": { + "description": "Numeric bound the displayed value is compared against. Only finite numbers are accepted (Infinity and NaN are rejected).", + "type": "number", + "example": 100 + }, + "color": { + "description": "Color applied when the rule matches.", + "type": "string", + "enum": [ + "chart-blue", + "chart-orange", + "chart-red", + "chart-cyan", + "chart-green", + "chart-pink", + "chart-purple", + "chart-light-blue", + "chart-brown", + "chart-gray", + "chart-success", + "chart-warning", + "chart-error" + ] + }, + "label": { + "description": "Optional label describing the rule.", + "type": "string", + "example": "High" + } + }, + "required": [ + "operator", + "value", + "color" + ] + }, + "ClickStackBetweenColorCondition": { + "properties": { + "operator": { + "description": "Range comparison operator.", + "type": "string", + "enum": [ + "between" + ], + "example": "between" + }, + "value": { + "type": "array", + "description": "Inclusive [min, max] range. Both bounds must be finite numbers.", + "items": { + "type": "number" + }, + "example": [ + 100, + 500 + ] + }, + "color": { + "description": "Color applied when the rule matches.", + "type": "string", + "enum": [ + "chart-blue", + "chart-orange", + "chart-red", + "chart-cyan", + "chart-green", + "chart-pink", + "chart-purple", + "chart-light-blue", + "chart-brown", + "chart-gray", + "chart-success", + "chart-warning", + "chart-error" + ] + }, + "label": { + "description": "Optional label describing the rule.", + "type": "string", + "example": "Warning" + } + }, + "required": [ + "operator", + "value", + "color" + ] + }, + "ClickStackEqualityColorCondition": { + "properties": { + "operator": { + "description": "Equality comparison operator.", + "type": "string", + "enum": [ + "eq", + "neq" + ], + "example": "eq" + }, + "value": { + "description": "A finite number, or a string up to 200 characters, to compare for equality.", + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "color": { + "description": "Color applied when the rule matches.", + "type": "string", + "enum": [ + "chart-blue", + "chart-orange", + "chart-red", + "chart-cyan", + "chart-green", + "chart-pink", + "chart-purple", + "chart-light-blue", + "chart-brown", + "chart-gray", + "chart-success", + "chart-warning", + "chart-error" + ] + }, + "label": { + "description": "Optional label describing the rule.", + "type": "string", + "example": "Healthy" + } + }, + "required": [ + "operator", + "value", + "color" + ] + }, + "ClickStackNumberTileColorCondition": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackNumericColorCondition" + }, + { + "$ref": "#/components/schemas/ClickStackBetweenColorCondition" + }, + { + "$ref": "#/components/schemas/ClickStackEqualityColorCondition" + } + ] + }, + "ClickStackBackgroundChart": { + "properties": { + "type": { + "description": "Sparkline shape.", + "type": "string", + "enum": [ + "line", + "area" + ], + "example": "line" + }, + "color": { + "description": "Optional palette-token override for the sparkline. When unset the sparkline inherits the tile's static color.", + "type": "string", + "enum": [ + "chart-blue", + "chart-orange", + "chart-red", + "chart-cyan", + "chart-green", + "chart-pink", + "chart-purple", + "chart-light-blue", + "chart-brown", + "chart-gray", + "chart-success", + "chart-warning", + "chart-error" + ] + } + }, + "required": [ + "type" + ] + }, + "ClickStackNumberBuilderChartConfig": { + "properties": { + "displayType": { + "description": "Display type discriminator. Must be \"number\" for single big-number charts.", + "type": "string", + "enum": [ + "number" + ], + "example": "number" + }, + "sourceId": { + "description": "ID of the data source to query.", + "type": "string", + "example": "65f5e4a3b9e77c001a111111" + }, + "select": { + "type": "array", + "description": "Exactly one aggregated value to display as a single number — unless \"formulas\" is set, in which case the select items are the formula's operands and the (single) formula value is displayed instead.", + "items": { + "$ref": "#/components/schemas/ClickStackSelectItem" + } + }, + "formulas": { + "type": "array", + "description": "A single derived value computed from the select items via letter-ref arithmetic (\"A\" = select[0], \"B\" = select[1], ...). Metric, log, and trace sources only. Number tiles display the formula value and always hide the operand series.", + "items": { + "$ref": "#/components/schemas/ClickStackFormula" + } + }, + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" + }, + "color": { + "description": "Optional static color applied to the displayed number.", + "type": "string", + "enum": [ + "chart-blue", + "chart-orange", + "chart-red", + "chart-cyan", + "chart-green", + "chart-pink", + "chart-purple", + "chart-light-blue", + "chart-brown", + "chart-gray", + "chart-success", + "chart-warning", + "chart-error" + ] + }, + "colorRules": { + "type": "array", + "description": "Ordered conditional color rules evaluated against the displayed value (last match wins). Falls back to color, then the default text color when no rule matches.", + "items": { + "$ref": "#/components/schemas/ClickStackNumberTileColorCondition" + } + }, + "backgroundChart": { + "$ref": "#/components/schemas/ClickStackBackgroundChart" + } + }, + "required": [ + "displayType", + "sourceId", + "select" + ] + }, + "ClickStackNumberRawSqlChartConfig": { + "properties": { + "configType": { + "description": "Must be \"sql\" to use the Raw SQL chart config variant.", + "type": "string", + "enum": [ + "sql" + ], + "example": "sql" + }, + "connectionId": { + "description": "ID of the ClickHouse connection to execute the query against.", + "type": "string", + "example": "65f5e4a3b9e77c001a567890" + }, + "sqlTemplate": { + "description": "SQL query template to execute. Supports HyperDX template variables.", + "type": "string", + "example": "SELECT count() FROM otel_logs WHERE timestamp > now() - INTERVAL 1 HOUR" + }, + "sourceId": { + "description": "Optional ID of the data source associated with this Raw SQL chart. Used for applying dashboard filters.", + "type": "string", + "example": "65f5e4a3b9e77c001a567890" + }, + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" + }, + "displayType": { + "description": "Display as a single big-number chart.", + "type": "string", + "enum": [ + "number" + ], + "example": "number" + }, + "color": { + "description": "Optional static color applied to the displayed number.", + "type": "string", + "enum": [ + "chart-blue", + "chart-orange", + "chart-red", + "chart-cyan", + "chart-green", + "chart-pink", + "chart-purple", + "chart-light-blue", + "chart-brown", + "chart-gray", + "chart-success", + "chart-warning", + "chart-error" + ] + }, + "colorRules": { + "type": "array", + "description": "Ordered conditional color rules evaluated against the displayed value (last match wins). Falls back to color, then the default text color when no rule matches.", + "items": { + "$ref": "#/components/schemas/ClickStackNumberTileColorCondition" + } + } + }, + "required": [ + "configType", + "connectionId", + "sqlTemplate", + "displayType" + ] + }, + "ClickStackNumberChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackNumberBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackNumberRawSqlChartConfig" + } + ] + }, + "ClickStackAlertNumberBuilderChartConfig": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackNumberBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertChartConfigOverlay" + } + ] + }, + "ClickStackAlertNumberRawSqlChartConfig": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackNumberRawSqlChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertChartConfigOverlay" + } + ] + }, + "ClickStackAlertNumberChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackAlertNumberBuilderChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertNumberRawSqlChartConfig" + } + ] + }, + "ClickStackAlertChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackAlertLineChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertBarChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertStackedLineChartConfig" + }, + { + "$ref": "#/components/schemas/ClickStackAlertNumberChartConfig" + } + ] + }, + "ClickStackAlertExecutionError": { + "properties": { + "timestamp": { + "description": "When the error occurred.", + "type": "string", + "format": "date-time", + "example": "2026-04-17T12:00:00.000Z" + }, + "type": { + "description": "Category of the error.", + "type": "string", + "enum": [ + "QUERY_ERROR", + "QUERY_TIMEOUT", + "WEBHOOK_ERROR", + "INVALID_ALERT", + "UNKNOWN" + ], + "example": "QUERY_ERROR" + }, + "message": { + "description": "Human-readable error message.", + "type": "string", + "example": "Query timed out after 30s" + } + }, + "required": [ + "timestamp", + "type", + "message" + ] + }, + "ClickStackAlertSilenced": { + "properties": { + "by": { + "description": "User ID who silenced the alert.", + "type": [ + "string", + "null" + ], + "example": "65f5e4a3b9e77c001a234567" + }, + "at": { + "description": "Silence start timestamp.", + "type": "string", + "format": "date-time", + "example": "2026-03-19T08:00:00.000Z" + }, + "until": { + "description": "Silence end timestamp.", + "type": "string", + "format": "date-time", + "example": "2026-03-20T08:00:00.000Z" + } + } + }, + "ClickStackAlertChannelEmail": { + "properties": { + "type": { + "description": "Channel type. Must be \"email\" for email alerts.", + "type": "string", + "enum": [ + "webhook", + "email" + ] + }, + "emailRecipients": { + "type": "array", + "description": "Email recipients for email alerts.", + "items": { + "type": "string" + } + } + }, + "required": [ + "type", + "emailRecipients" + ] + }, + "ClickStackAlertChannelWebhook": { + "properties": { + "type": { + "description": "Channel type. Must be \"webhook\" for webhook alerts.", + "type": "string", + "enum": [ + "webhook", + "email" + ] + }, + "webhookId": { + "description": "Webhook destination ID.", + "type": "string", + "example": "65f5e4a3b9e77c001a789012" + }, + "webhookService": { + "description": "Webhook service type (e.g., slack_api).", + "type": [ + "string", + "null" + ], + "example": "slack_api" + }, + "slackChannelId": { + "description": "Slack channel ID for Slack webhooks.", + "type": [ + "string", + "null" + ], + "example": "C01ABCDEF23" + }, + "severity": { + "description": "Severity label used by PagerDuty API webhooks.", + "type": [ + "string", + "null" + ], + "enum": [ + "critical", + "error", + "warning", + "info" + ] + } + }, + "required": [ + "type", + "webhookId" + ] + }, + "ClickStackAlertChannel": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackAlertChannelEmail" + }, + { + "$ref": "#/components/schemas/ClickStackAlertChannelWebhook" + } + ] + }, + "ClickStackAlertChannels": { + "type": "array", + "description": "Notification channels to trigger when the alert fires or resolves. Between 1 and 10 channels; duplicates are rejected.", + "minItems": 1, + "maxItems": 10, + "items": { + "$ref": "#/components/schemas/ClickStackAlertChannel" + } + }, + "ClickStackAlertResponse": { + "properties": { + "dashboardId": { + "description": "Dashboard ID for tile-based alerts.", + "type": [ + "string", + "null" + ], + "example": "65f5e4a3b9e77c001a567890" + }, + "tileId": { + "description": "Tile ID for tile-based alerts. Must be a line, stacked bar, or number type tile.", + "type": [ + "string", + "null" + ], + "example": "65f5e4a3b9e77c001a901234" + }, + "savedSearchId": { + "description": "Saved search ID for saved_search alerts.", + "type": [ + "string", + "null" + ], + "example": "65f5e4a3b9e77c001a345678" + }, + "groupBy": { + "description": "Group-by key for saved search alerts.", + "type": [ + "string", + "null" + ], + "example": "ServiceName" + }, + "chartConfig": { + "$ref": "#/components/schemas/ClickStackAlertChartConfig" + }, + "threshold": { + "description": "Threshold value for triggering the alert. For between and not_between threshold types, this is the lower bound.", + "type": "number", + "example": 100 + }, + "thresholdMax": { + "description": "Upper bound for between and not_between threshold types. Required when thresholdType is between or not_between, must be >= threshold.", + "type": [ + "number", + "null" + ], + "example": 500 + }, + "interval": { + "description": "Evaluation interval for the alert. `30s` requires the 30s alert interval feature to be enabled for your team. Anomaly alerts (detectionMode \"anomaly\") support only `5m`, `15m`, `30m`, and `1h`; other intervals are rejected with 400.", + "type": "string", + "enum": [ + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "6h", + "12h", + "1d" + ], + "example": "1h" + }, + "scheduleOffsetMinutes": { + "description": "Offset from the interval boundary in minutes. For example, 2 with a 5m interval evaluates windows at :02, :07, :12, etc. (UTC).", + "type": [ + "integer", + "null" + ], + "example": 2 + }, + "scheduleStartAt": { + "description": "Absolute UTC start time anchor. Alert windows start from this timestamp and repeat every interval.", + "type": [ + "string", + "null" + ], + "format": "date-time", + "example": "2026-02-08T10:00:00.000Z" + }, + "source": { + "description": "Alert source type (tile-based or saved search).", + "type": "string", + "enum": [ + "saved_search", + "tile", + "inline" + ], + "example": "tile" + }, + "thresholdType": { + "description": "Threshold comparison direction.", + "type": "string", + "enum": [ + "above", + "below", + "above_exclusive", + "below_or_equal", + "equal", + "not_equal", + "between", + "not_between" + ], + "example": "above" + }, + "channel": { + "$ref": "#/components/schemas/ClickStackAlertChannel" + }, + "channels": { + "$ref": "#/components/schemas/ClickStackAlertChannels" + }, + "name": { + "description": "Alert name template (Handlebars), rendered as the notification title. When omitted, a default title is generated from the display name, value and threshold.", + "type": [ + "string", + "null" + ], + "example": "Errors for {{group}} hit {{value}}" + }, + "message": { + "description": "Alert message template.", + "type": [ + "string", + "null" + ], + "example": "Test Alert Message" + }, + "note": { + "description": "Freeform note for the alert. Supports markdown formatting.", + "type": [ + "string", + "null" + ], + "example": "Threshold raised from 50 to 100 on 2026-01-15. See [runbook](https://wiki.example.com/runbook)." + }, + "numConsecutiveWindows": { + "description": "Fire the alert only after its condition has been met for this many consecutive evaluation windows. While the condition is met but fewer than this many consecutive windows have violated, the alert is in the PENDING state.", + "type": [ + "integer", + "null" + ], + "example": 3 + }, + "displayName": { + "description": "The display name for the alert in the UI. Derived from the saved search name, dashboard tile name, or inline chartConfig name when not explicitly set.", + "type": "string", + "example": "Checkout error spike" + }, + "tags": { + "type": "array", + "description": "The tags for the alert. Derived from the tags of the referenced saved search or dashboard when not explicitly set; inline alerts default to an empty list.", + "items": { + "type": "string" + }, + "example": [ + "checkout", + "p1" + ] + }, + "id": { + "description": "Unique alert identifier.", + "type": "string", + "example": "65f5e4a3b9e77c001a123456" + }, + "state": { + "description": "Current alert state.", "type": "string", "enum": [ "ALERT", @@ -32150,7 +33461,11 @@ "format": "date-time", "example": "2023-01-01T00:00:00.000Z" } - } + }, + "required": [ + "displayName", + "tags" + ] }, "ClickStackCreateAlertRequest": { "properties": { @@ -32186,6 +33501,9 @@ ], "example": "ServiceName" }, + "chartConfig": { + "$ref": "#/components/schemas/ClickStackAlertChartConfig" + }, "threshold": { "description": "Threshold value for triggering the alert. For between and not_between threshold types, this is the lower bound.", "type": "number", @@ -32200,7 +33518,7 @@ "example": 500 }, "interval": { - "description": "Evaluation interval for the alert. `30s` requires the 30s alert interval feature to be enabled for your team.", + "description": "Evaluation interval for the alert. `30s` requires the 30s alert interval feature to be enabled for your team. Anomaly alerts (detectionMode \"anomaly\") support only `5m`, `15m`, `30m`, and `1h`; other intervals are rejected with 400.", "type": "string", "enum": [ "30s", @@ -32237,7 +33555,8 @@ "type": "string", "enum": [ "saved_search", - "tile" + "tile", + "inline" ], "example": "tile" }, @@ -32263,12 +33582,31 @@ "$ref": "#/components/schemas/ClickStackAlertChannels" }, "name": { - "description": "Human-friendly alert name.", + "description": "Alert name template (Handlebars), rendered as the notification title. When omitted, a default title is generated from the display name, value and threshold.", + "type": [ + "string", + "null" + ], + "example": "Errors for {{group}} hit {{value}}" + }, + "displayName": { + "description": "Display name shown in the alerts list and in notification titles. Defaults to the name of the referenced saved search, dashboard tile, or inline chart when omitted or null.", "type": [ "string", "null" ], - "example": "Test Alert" + "example": "Checkout error spike" + }, + "tags": { + "type": "array", + "description": "Tags for the alert. Defaults to the tags of the referenced saved search or dashboard when omitted or null; inline alerts have no parent to inherit from and default to an empty list.", + "items": { + "type": "string" + }, + "example": [ + "checkout", + "p1" + ] }, "message": { "description": "Alert message template.", @@ -32294,7 +33632,12 @@ ], "example": 3 } - } + }, + "required": [ + "threshold", + "interval", + "thresholdType" + ] }, "ClickStackUpdateAlertRequest": { "properties": { @@ -32330,6 +33673,9 @@ ], "example": "ServiceName" }, + "chartConfig": { + "$ref": "#/components/schemas/ClickStackAlertChartConfig" + }, "threshold": { "description": "Threshold value for triggering the alert. For between and not_between threshold types, this is the lower bound.", "type": "number", @@ -32344,7 +33690,7 @@ "example": 500 }, "interval": { - "description": "Evaluation interval for the alert. `30s` requires the 30s alert interval feature to be enabled for your team.", + "description": "Evaluation interval for the alert. `30s` requires the 30s alert interval feature to be enabled for your team. Anomaly alerts (detectionMode \"anomaly\") support only `5m`, `15m`, `30m`, and `1h`; other intervals are rejected with 400.", "type": "string", "enum": [ "30s", @@ -32381,7 +33727,8 @@ "type": "string", "enum": [ "saved_search", - "tile" + "tile", + "inline" ], "example": "tile" }, @@ -32407,12 +33754,31 @@ "$ref": "#/components/schemas/ClickStackAlertChannels" }, "name": { - "description": "Human-friendly alert name.", + "description": "Alert name template (Handlebars), rendered as the notification title. When omitted, a default title is generated from the display name, value and threshold.", + "type": [ + "string", + "null" + ], + "example": "Errors for {{group}} hit {{value}}" + }, + "displayName": { + "description": "Display name shown in the alerts list and in notification titles. Defaults to the name of the referenced saved search, dashboard tile, or inline chart when omitted or null.", "type": [ "string", "null" ], - "example": "Test Alert" + "example": "Checkout error spike" + }, + "tags": { + "type": "array", + "description": "Tags for the alert. Defaults to the tags of the referenced saved search or dashboard when omitted or null; inline alerts have no parent to inherit from and default to an empty list.", + "items": { + "type": "string" + }, + "example": [ + "checkout", + "p1" + ] }, "message": { "description": "Alert message template.", @@ -32438,7 +33804,12 @@ ], "example": 3 } - } + }, + "required": [ + "threshold", + "interval", + "thresholdType" + ] }, "ClickStackSqlSavedFilterValue": { "properties": { @@ -32477,340 +33848,33 @@ }, "values": { "type": "array", - "description": "Selected values", + "description": "Selected values. Ignored when \"all\" is true.", "items": { "type": "string" }, "example": [ "hdx-oss-dev-api" ] - } - }, - "required": [ - "type", - "name", - "values" - ] - }, - "ClickStackSavedFilterValue": { - "oneOf": [ - { - "$ref": "#/components/schemas/ClickStackSqlSavedFilterValue" - }, - { - "$ref": "#/components/schemas/ClickStackVariableSavedFilterValue" - } - ] - }, - "ClickStackNumberFormat": { - "properties": { - "output": { - "description": "Output format applied to the number.", - "type": "string", - "enum": [ - "currency", - "percent", - "byte", - "time", - "number", - "data_rate", - "throughput", - "duration" - ], - "example": "number" - }, - "mantissa": { - "description": "Number of decimal places.", - "type": "integer", - "example": 2 }, - "thousandSeparated": { - "description": "Whether to use thousand separators.", + "all": { + "description": "Set to true to select every available value rather than a fixed list, which only SOURCE_FILTER filters offer (and only for columns their source declares with allowAll). Send an empty \"values\" alongside it. Allowed values: true.", "type": "boolean", "example": true - }, - "average": { - "description": "Whether to show as average.", - "type": "boolean", - "example": false - }, - "decimalBytes": { - "description": "Use decimal bytes (1000) vs binary bytes (1024).", - "type": "boolean", - "example": false - }, - "factor": { - "description": "Multiplication factor.", - "type": "number", - "example": 1 - }, - "currencySymbol": { - "description": "Currency symbol for currency format.", - "type": "string", - "example": "$" - }, - "numericUnit": { - "description": "Numeric unit for data, data rate, or throughput formats.", - "type": "string", - "enum": [ - "bytes_iec", - "bytes_si", - "bits_iec", - "bits_si", - "kibibytes", - "kilobytes", - "mebibytes", - "megabytes", - "gibibytes", - "gigabytes", - "tebibytes", - "terabytes", - "pebibytes", - "petabytes", - "packets_sec", - "bytes_sec_iec", - "bytes_sec_si", - "bits_sec_iec", - "bits_sec_si", - "kibibytes_sec", - "kibibits_sec", - "kilobytes_sec", - "kilobits_sec", - "mebibytes_sec", - "mebibits_sec", - "megabytes_sec", - "megabits_sec", - "gibibytes_sec", - "gibibits_sec", - "gigabytes_sec", - "gigabits_sec", - "tebibytes_sec", - "tebibits_sec", - "terabytes_sec", - "terabits_sec", - "pebibytes_sec", - "pebibits_sec", - "petabytes_sec", - "petabits_sec", - "cps", - "ops", - "rps", - "reads_sec", - "wps", - "iops", - "cpm", - "opm", - "rpm_reads", - "wpm" - ], - "example": "bytes_iec" - }, - "unit": { - "description": "Custom unit label.", - "type": "string", - "example": "ms" - } - } - }, - "ClickStackBackgroundChart": { - "properties": { - "type": { - "description": "Sparkline shape.", - "type": "string", - "enum": [ - "line", - "area" - ], - "example": "line" - }, - "color": { - "description": "Optional palette-token override for the sparkline. When unset the sparkline inherits the tile's static color.", - "type": "string", - "enum": [ - "chart-blue", - "chart-orange", - "chart-red", - "chart-cyan", - "chart-green", - "chart-pink", - "chart-purple", - "chart-light-blue", - "chart-brown", - "chart-gray", - "chart-success", - "chart-warning", - "chart-error" - ] - } - }, - "required": [ - "type" - ] - }, - "ClickStackNumericColorCondition": { - "properties": { - "operator": { - "description": "Numeric comparison operator.", - "type": "string", - "enum": [ - "gt", - "gte", - "lt", - "lte" - ], - "example": "gt" - }, - "value": { - "description": "Numeric bound the displayed value is compared against. Only finite numbers are accepted (Infinity and NaN are rejected).", - "type": "number", - "example": 100 - }, - "color": { - "description": "Color applied when the rule matches.", - "type": "string", - "enum": [ - "chart-blue", - "chart-orange", - "chart-red", - "chart-cyan", - "chart-green", - "chart-pink", - "chart-purple", - "chart-light-blue", - "chart-brown", - "chart-gray", - "chart-success", - "chart-warning", - "chart-error" - ] - }, - "label": { - "description": "Optional label describing the rule.", - "type": "string", - "example": "High" - } - }, - "required": [ - "operator", - "value", - "color" - ] - }, - "ClickStackBetweenColorCondition": { - "properties": { - "operator": { - "description": "Range comparison operator.", - "type": "string", - "enum": [ - "between" - ], - "example": "between" - }, - "value": { - "type": "array", - "description": "Inclusive [min, max] range. Both bounds must be finite numbers.", - "items": { - "type": "number" - }, - "example": [ - 100, - 500 - ] - }, - "color": { - "description": "Color applied when the rule matches.", - "type": "string", - "enum": [ - "chart-blue", - "chart-orange", - "chart-red", - "chart-cyan", - "chart-green", - "chart-pink", - "chart-purple", - "chart-light-blue", - "chart-brown", - "chart-gray", - "chart-success", - "chart-warning", - "chart-error" - ] - }, - "label": { - "description": "Optional label describing the rule.", - "type": "string", - "example": "Warning" } }, "required": [ - "operator", - "value", - "color" - ] - }, - "ClickStackEqualityColorCondition": { - "properties": { - "operator": { - "description": "Equality comparison operator.", - "type": "string", - "enum": [ - "eq", - "neq" - ], - "example": "eq" - }, - "value": { - "description": "A finite number, or a string up to 200 characters, to compare for equality.", - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] - }, - "color": { - "description": "Color applied when the rule matches.", - "type": "string", - "enum": [ - "chart-blue", - "chart-orange", - "chart-red", - "chart-cyan", - "chart-green", - "chart-pink", - "chart-purple", - "chart-light-blue", - "chart-brown", - "chart-gray", - "chart-success", - "chart-warning", - "chart-error" - ] - }, - "label": { - "description": "Optional label describing the rule.", - "type": "string", - "example": "Healthy" - } - }, - "required": [ - "operator", - "value", - "color" + "type", + "name", + "values" ] }, - "ClickStackNumberTileColorCondition": { - "oneOf": [ - { - "$ref": "#/components/schemas/ClickStackNumericColorCondition" - }, + "ClickStackSavedFilterValue": { + "oneOf": [ { - "$ref": "#/components/schemas/ClickStackBetweenColorCondition" + "$ref": "#/components/schemas/ClickStackSqlSavedFilterValue" }, { - "$ref": "#/components/schemas/ClickStackEqualityColorCondition" + "$ref": "#/components/schemas/ClickStackVariableSavedFilterValue" } ] }, @@ -32910,6 +33974,7 @@ "type": "string", "enum": [ "stacked_bar", + "stacked_line", "line" ], "example": "line" @@ -33214,248 +34279,6 @@ } ] }, - "ClickStackSelectItem": { - "properties": { - "aggFn": { - "description": "Aggregation function to apply. \"count\" does not require a valueExpression; \"quantile\" requires a level field indicating the desired percentile (e.g., 0.95).", - "type": "string", - "enum": [ - "avg", - "count", - "count_distinct", - "last_value", - "max", - "min", - "quantile", - "sum", - "any", - "none" - ], - "example": "count" - }, - "valueExpression": { - "description": "Expression for the column or value to aggregate. Must be omitted when aggFn is \"count\"; required for all other aggFn values.", - "type": "string", - "example": "Duration" - }, - "alias": { - "description": "Display alias for this select item in chart legends.", - "type": "string", - "example": "Request Duration" - }, - "level": { - "description": "Percentile level; only valid when aggFn is \"quantile\".", - "type": "number", - "enum": [ - "0.5", - "0.9", - "0.95", - "0.99" - ] - }, - "where": { - "description": "SQL or Lucene filter condition applied before aggregation.", - "type": "string", - "example": "service:api" - }, - "whereLanguage": { - "description": "Query language for the where clause.", - "type": "string", - "enum": [ - "sql", - "lucene" - ] - }, - "metricName": { - "description": "Name of the metric to aggregate; only applicable when the source is a metrics source.", - "type": "string", - "example": "http.server.duration" - }, - "metricType": { - "description": "Metric type; only applicable when the source is a metrics source.", - "type": "string", - "enum": [ - "sum", - "gauge", - "histogram", - "summary", - "exponential histogram" - ] - }, - "periodAggFn": { - "description": "Optional period aggregation function for Gauge metrics (e.g., compute the delta over the period).", - "type": "string", - "enum": [ - "delta" - ], - "example": "delta" - }, - "numberFormat": { - "$ref": "#/components/schemas/ClickStackNumberFormat" - } - }, - "required": [ - "aggFn" - ] - }, - "ClickStackFormula": { - "properties": { - "expression": { - "description": "Arithmetic expression over the select items by position, e.g. \"A / (A + B) * 100\" for a success-rate percentage.", - "type": "string", - "example": "A / (A + B) * 100" - }, - "alias": { - "description": "Display label for the formula series in chart legends and column headers. Falls back to the raw expression text when unset.", - "type": "string", - "example": "Success rate %" - }, - "numberFormat": { - "$ref": "#/components/schemas/ClickStackNumberFormat" - } - }, - "required": [ - "expression" - ] - }, - "ClickStackLineBuilderChartConfig": { - "properties": { - "displayType": { - "description": "Display type discriminator. Must be \"line\" for line charts.", - "type": "string", - "enum": [ - "line" - ], - "example": "line" - }, - "sourceId": { - "description": "ID of the data source to query.", - "type": "string", - "example": "65f5e4a3b9e77c001a111111" - }, - "select": { - "type": "array", - "description": "One or more aggregated values to plot. When asRatio is true, exactly two select items are required.", - "items": { - "$ref": "#/components/schemas/ClickStackSelectItem" - } - }, - "groupBy": { - "description": "Field expression to group results by (creates separate lines per group value).", - "type": "string", - "example": "host" - }, - "asRatio": { - "description": "Plot select[0] / select[1] as a ratio. Requires exactly two select items.", - "type": "boolean" - }, - "alignDateRangeToGranularity": { - "description": "Expand date range boundaries to the query granularity interval.", - "type": "boolean" - }, - "fillNulls": { - "description": "Fill missing time buckets with zero instead of leaving gaps.", - "type": "boolean" - }, - "fitYAxisToData": { - "description": "Set the y-axis lower bound to the minimum of the displayed data instead of zero, making small fluctuations between series easier to see.", - "type": "boolean" - }, - "numberFormat": { - "$ref": "#/components/schemas/ClickStackNumberFormat" - }, - "compareToPreviousPeriod": { - "description": "Overlay the equivalent previous time period for comparison.", - "type": "boolean" - }, - "seriesLimit": { - "description": "Maximum number of series rendered (top-N by value). Omit to use the default render cap, set 0 for unlimited, or a positive N to keep the top N series.", - "type": "integer", - "example": 5 - }, - "formulas": { - "type": "array", - "description": "Derived series computed from the select items via letter-ref arithmetic (\"A\" = select[0], \"B\" = select[1], ...). Metric, log, and trace sources only. Cannot be combined with asRatio.", - "items": { - "$ref": "#/components/schemas/ClickStackFormula" - } - }, - "showOperandSeries": { - "description": "Only meaningful with formulas. When false, only the formula series are returned; the raw operand series are hidden.", - "type": "boolean" - } - }, - "required": [ - "displayType", - "sourceId", - "select" - ] - }, - "ClickStackBarBuilderChartConfig": { - "properties": { - "displayType": { - "description": "Display type discriminator. Must be \"stacked_bar\" for stacked-bar charts.", - "type": "string", - "enum": [ - "stacked_bar" - ], - "example": "stacked_bar" - }, - "sourceId": { - "description": "ID of the data source to query.", - "type": "string", - "example": "65f5e4a3b9e77c001a111111" - }, - "select": { - "type": "array", - "description": "One or more aggregated values to plot. When asRatio is true, exactly two select items are required.", - "items": { - "$ref": "#/components/schemas/ClickStackSelectItem" - } - }, - "groupBy": { - "description": "Field expression to group results by (creates separate bars segments per group value).", - "type": "string", - "example": "service" - }, - "asRatio": { - "description": "Plot select[0] / select[1] as a ratio. Requires exactly two select items.", - "type": "boolean" - }, - "alignDateRangeToGranularity": { - "description": "Align the date range boundaries to the query granularity interval.", - "type": "boolean" - }, - "fillNulls": { - "description": "Fill missing time buckets with zero instead of leaving gaps.", - "type": "boolean" - }, - "numberFormat": { - "$ref": "#/components/schemas/ClickStackNumberFormat" - }, - "seriesLimit": { - "description": "Maximum number of series rendered (top-N by value). Omit to use the default render cap, set 0 for unlimited, or a positive N to keep the top N series.", - "type": "integer", - "example": 5 - }, - "formulas": { - "type": "array", - "description": "Derived series computed from the select items via letter-ref arithmetic (\"A\" = select[0], \"B\" = select[1], ...). Metric, log, and trace sources only. Cannot be combined with asRatio.", - "items": { - "$ref": "#/components/schemas/ClickStackFormula" - } - }, - "showOperandSeries": { - "description": "Only meaningful with formulas. When false, only the formula series are returned; the raw operand series are hidden.", - "type": "boolean" - } - }, - "required": [ - "displayType", - "sourceId", - "select" - ] - }, "ClickStackOnClickTargetIdVariant": { "properties": { "mode": { @@ -33718,74 +34541,6 @@ "select" ] }, - "ClickStackNumberBuilderChartConfig": { - "properties": { - "displayType": { - "description": "Display type discriminator. Must be \"number\" for single big-number charts.", - "type": "string", - "enum": [ - "number" - ], - "example": "number" - }, - "sourceId": { - "description": "ID of the data source to query.", - "type": "string", - "example": "65f5e4a3b9e77c001a111111" - }, - "select": { - "type": "array", - "description": "Exactly one aggregated value to display as a single number — unless \"formulas\" is set, in which case the select items are the formula's operands and the (single) formula value is displayed instead.", - "items": { - "$ref": "#/components/schemas/ClickStackSelectItem" - } - }, - "formulas": { - "type": "array", - "description": "A single derived value computed from the select items via letter-ref arithmetic (\"A\" = select[0], \"B\" = select[1], ...). Metric, log, and trace sources only. Number tiles display the formula value and always hide the operand series.", - "items": { - "$ref": "#/components/schemas/ClickStackFormula" - } - }, - "numberFormat": { - "$ref": "#/components/schemas/ClickStackNumberFormat" - }, - "color": { - "description": "Optional static color applied to the displayed number.", - "type": "string", - "enum": [ - "chart-blue", - "chart-orange", - "chart-red", - "chart-cyan", - "chart-green", - "chart-pink", - "chart-purple", - "chart-light-blue", - "chart-brown", - "chart-gray", - "chart-success", - "chart-warning", - "chart-error" - ] - }, - "colorRules": { - "type": "array", - "description": "Ordered conditional color rules evaluated against the displayed value (last match wins). Falls back to color, then the default text color when no rule matches.", - "items": { - "$ref": "#/components/schemas/ClickStackNumberTileColorCondition" - } - }, - "backgroundChart": { - "$ref": "#/components/schemas/ClickStackBackgroundChart" - } - }, - "required": [ - "displayType", - "sourceId", - "select" - ] - }, "ClickStackPieBuilderChartConfig": { "properties": { "displayType": { @@ -33906,7 +34661,7 @@ "valueExpression" ] }, - "ClickStackHeatmapChartConfig": { + "ClickStackHeatmapDistributionChartConfig": { "properties": { "displayType": { "description": "Display type discriminator. Must be \"heatmap\" for heatmap tiles.", @@ -33916,6 +34671,14 @@ ], "example": "heatmap" }, + "heatmapMode": { + "description": "Heatmap mode. \"distribution\" is the default when omitted.", + "type": "string", + "enum": [ + "distribution" + ], + "example": "distribution" + }, "sourceId": { "description": "ID of the data source to query.", "type": "string", @@ -33951,266 +34714,163 @@ "select" ] }, - "ClickStackSearchChartConfig": { + "ClickStackHeatmapSeriesChartConfig": { "properties": { "displayType": { - "description": "Display type discriminator. Must be \"search\" for search/log viewer tiles.", + "description": "Display type discriminator. Must be \"heatmap\" for heatmap tiles.", "type": "string", "enum": [ - "search" + "heatmap" ], - "example": "search" - }, - "sourceId": { - "description": "ID of the data source to query.", - "type": "string", - "example": "65f5e4a3b9e77c001a111111" - }, - "select": { - "description": "Comma-separated list of expressions to display.", - "type": "string", - "example": "timestamp, level, message" - }, - "where": { - "description": "Filter condition for the search (syntax depends on whereLanguage).", - "type": "string", - "example": "level:error" + "example": "heatmap" }, - "whereLanguage": { - "description": "Query language for the where clause.", - "type": "string", - "enum": [ - "sql", - "lucene" - ] - } - }, - "required": [ - "displayType", - "sourceId", - "select", - "whereLanguage" - ] - }, - "ClickStackEventPatternsChartConfig": { - "properties": { - "displayType": { - "description": "Display type discriminator. Must be \"event_patterns\" for pattern mining tiles.", + "heatmapMode": { + "description": "Heatmap mode. Must be \"series\" for series heatmaps.", "type": "string", "enum": [ - "event_patterns" + "series" ], - "example": "event_patterns" + "example": "series" }, "sourceId": { - "description": "ID of the data source to mine patterns from.", + "description": "ID of the data source to query.", "type": "string", "example": "65f5e4a3b9e77c001a111111" }, "select": { - "description": "Column or expression to mine patterns from. Leave empty to use the source default (Body for logs, SpanName for traces).", - "type": "string", - "example": "Body" + "type": "array", + "description": "Exactly one aggregated value used to color each cell.", + "items": { + "$ref": "#/components/schemas/ClickStackSelectItem" + } }, - "where": { - "description": "Filter condition for the pattern mining query (syntax depends on whereLanguage).", + "groupBy": { + "description": "Field expression to group results by (one row per group value).", "type": "string", - "example": "level:error" + "example": "ServiceName" }, - "whereLanguage": { - "description": "Query language for the where clause.", - "type": "string", - "enum": [ - "sql", - "lucene" - ] + "numberFormat": { + "$ref": "#/components/schemas/ClickStackNumberFormat" } }, "required": [ "displayType", - "sourceId" + "heatmapMode", + "sourceId", + "select" ] }, - "ClickStackMarkdownChartConfig": { - "properties": { - "displayType": { - "description": "Display type discriminator. Must be \"markdown\" for markdown text tiles.", - "type": "string", - "enum": [ - "markdown" - ], - "example": "markdown" + "ClickStackHeatmapChartConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackHeatmapDistributionChartConfig" }, - "markdown": { - "description": "Markdown content to render inside the tile.", - "type": "string", - "example": "# Dashboard Title\n\nThis is a markdown widget." + { + "$ref": "#/components/schemas/ClickStackHeatmapSeriesChartConfig" } - }, - "required": [ - "displayType" ] }, - "ClickStackLineRawSqlChartConfig": { + "ClickStackSearchChartConfig": { "properties": { - "configType": { - "description": "Must be \"sql\" to use the Raw SQL chart config variant.", - "type": "string", - "enum": [ - "sql" - ], - "example": "sql" - }, - "connectionId": { - "description": "ID of the ClickHouse connection to execute the query against.", - "type": "string", - "example": "65f5e4a3b9e77c001a567890" - }, - "sqlTemplate": { - "description": "SQL query template to execute. Supports HyperDX template variables.", - "type": "string", - "example": "SELECT count() FROM otel_logs WHERE timestamp > now() - INTERVAL 1 HOUR" - }, - "sourceId": { - "description": "Optional ID of the data source associated with this Raw SQL chart. Used for applying dashboard filters.", - "type": "string", - "example": "65f5e4a3b9e77c001a567890" - }, - "numberFormat": { - "$ref": "#/components/schemas/ClickStackNumberFormat" - }, "displayType": { - "description": "Display as a line time-series chart.", - "type": "string", - "enum": [ - "line" - ], - "example": "line" - }, - "compareToPreviousPeriod": { - "description": "Overlay the equivalent previous time period for comparison.", - "type": "boolean" - }, - "fillNulls": { - "description": "Fill missing time buckets with zero instead of leaving gaps.", - "type": "boolean" - }, - "alignDateRangeToGranularity": { - "description": "Expand date range boundaries to the query granularity interval.", - "type": "boolean" - }, - "fitYAxisToData": { - "description": "Set the y-axis lower bound to the minimum of the displayed data instead of zero, making small fluctuations between series easier to see.", - "type": "boolean" - } - }, - "required": [ - "configType", - "connectionId", - "sqlTemplate", - "displayType" - ] - }, - "ClickStackBarRawSqlChartConfig": { - "properties": { - "configType": { - "description": "Must be \"sql\" to use the Raw SQL chart config variant.", + "description": "Display type discriminator. Must be \"search\" for search/log viewer tiles.", "type": "string", "enum": [ - "sql" + "search" ], - "example": "sql" - }, - "connectionId": { - "description": "ID of the ClickHouse connection to execute the query against.", - "type": "string", - "example": "65f5e4a3b9e77c001a567890" - }, - "sqlTemplate": { - "description": "SQL query template to execute. Supports HyperDX template variables.", - "type": "string", - "example": "SELECT count() FROM otel_logs WHERE timestamp > now() - INTERVAL 1 HOUR" + "example": "search" }, "sourceId": { - "description": "Optional ID of the data source associated with this Raw SQL chart. Used for applying dashboard filters.", + "description": "ID of the data source to query.", "type": "string", - "example": "65f5e4a3b9e77c001a567890" - }, - "numberFormat": { - "$ref": "#/components/schemas/ClickStackNumberFormat" + "example": "65f5e4a3b9e77c001a111111" }, - "displayType": { - "description": "Display as a stacked-bar time-series chart.", + "select": { + "description": "Comma-separated list of expressions to display.", "type": "string", - "enum": [ - "stacked_bar" - ], - "example": "stacked_bar" + "example": "timestamp, level, message" }, - "fillNulls": { - "description": "Fill missing time buckets with zero instead of leaving gaps.", - "type": "boolean" + "where": { + "description": "Filter condition for the search (syntax depends on whereLanguage).", + "type": "string", + "example": "level:error" }, - "alignDateRangeToGranularity": { - "description": "Expand date range boundaries to the query granularity interval.", - "type": "boolean" + "whereLanguage": { + "description": "Query language for the where clause.", + "type": "string", + "enum": [ + "sql", + "lucene" + ] } }, "required": [ - "configType", - "connectionId", - "sqlTemplate", - "displayType" + "displayType", + "sourceId", + "select", + "whereLanguage" ] }, - "ClickStackTableRawSqlChartConfig": { + "ClickStackEventPatternsChartConfig": { "properties": { - "configType": { - "description": "Must be \"sql\" to use the Raw SQL chart config variant.", + "displayType": { + "description": "Display type discriminator. Must be \"event_patterns\" for pattern mining tiles.", "type": "string", "enum": [ - "sql" + "event_patterns" ], - "example": "sql" + "example": "event_patterns" }, - "connectionId": { - "description": "ID of the ClickHouse connection to execute the query against.", + "sourceId": { + "description": "ID of the data source to mine patterns from.", "type": "string", - "example": "65f5e4a3b9e77c001a567890" + "example": "65f5e4a3b9e77c001a111111" }, - "sqlTemplate": { - "description": "SQL query template to execute. Supports HyperDX template variables.", + "select": { + "description": "Column or expression to mine patterns from. Leave empty to use the source default (Body for logs, SpanName for traces).", "type": "string", - "example": "SELECT count() FROM otel_logs WHERE timestamp > now() - INTERVAL 1 HOUR" + "example": "Body" }, - "sourceId": { - "description": "Optional ID of the data source associated with this Raw SQL chart. Used for applying dashboard filters.", + "where": { + "description": "Filter condition for the pattern mining query (syntax depends on whereLanguage).", "type": "string", - "example": "65f5e4a3b9e77c001a567890" - }, - "numberFormat": { - "$ref": "#/components/schemas/ClickStackNumberFormat" + "example": "level:error" }, + "whereLanguage": { + "description": "Query language for the where clause.", + "type": "string", + "enum": [ + "sql", + "lucene" + ] + } + }, + "required": [ + "displayType", + "sourceId" + ] + }, + "ClickStackMarkdownChartConfig": { + "properties": { "displayType": { - "description": "Display as a table chart.", + "description": "Display type discriminator. Must be \"markdown\" for markdown text tiles.", "type": "string", "enum": [ - "table" + "markdown" ], - "example": "table" + "example": "markdown" }, - "onClick": { - "$ref": "#/components/schemas/ClickStackOnClick" + "markdown": { + "description": "Markdown content to render inside the tile.", + "type": "string", + "example": "# Dashboard Title\n\nThis is a markdown widget." } }, "required": [ - "configType", - "connectionId", - "sqlTemplate", "displayType" ] }, - "ClickStackNumberRawSqlChartConfig": { + "ClickStackTableRawSqlChartConfig": { "properties": { "configType": { "description": "Must be \"sql\" to use the Raw SQL chart config variant.", @@ -34239,31 +34899,15 @@ "$ref": "#/components/schemas/ClickStackNumberFormat" }, "displayType": { - "description": "Display as a single big-number chart.", + "description": "Display as a table chart.", "type": "string", "enum": [ - "number" + "table" ], - "example": "number" + "example": "table" }, - "color": { - "description": "Optional static color applied to the displayed number. Raw SQL number tiles do not support conditional colorRules.", - "type": "string", - "enum": [ - "chart-blue", - "chart-orange", - "chart-red", - "chart-cyan", - "chart-green", - "chart-pink", - "chart-purple", - "chart-light-blue", - "chart-brown", - "chart-gray", - "chart-success", - "chart-warning", - "chart-error" - ] + "onClick": { + "$ref": "#/components/schemas/ClickStackOnClick" } }, "required": [ @@ -34361,26 +35005,6 @@ "displayType" ] }, - "ClickStackLineChartConfig": { - "oneOf": [ - { - "$ref": "#/components/schemas/ClickStackLineBuilderChartConfig" - }, - { - "$ref": "#/components/schemas/ClickStackLineRawSqlChartConfig" - } - ] - }, - "ClickStackBarChartConfig": { - "oneOf": [ - { - "$ref": "#/components/schemas/ClickStackBarBuilderChartConfig" - }, - { - "$ref": "#/components/schemas/ClickStackBarRawSqlChartConfig" - } - ] - }, "ClickStackTableChartConfig": { "oneOf": [ { @@ -34391,16 +35015,6 @@ } ] }, - "ClickStackNumberChartConfig": { - "oneOf": [ - { - "$ref": "#/components/schemas/ClickStackNumberBuilderChartConfig" - }, - { - "$ref": "#/components/schemas/ClickStackNumberRawSqlChartConfig" - } - ] - }, "ClickStackPieChartConfig": { "oneOf": [ { @@ -34429,6 +35043,9 @@ { "$ref": "#/components/schemas/ClickStackBarChartConfig" }, + { + "$ref": "#/components/schemas/ClickStackStackedLineChartConfig" + }, { "$ref": "#/components/schemas/ClickStackTableChartConfig" }, @@ -34554,6 +35171,11 @@ "type": "string", "example": "errors" }, + "description": { + "description": "Short explanation of the tile, shown as a tooltip next to its name.", + "type": "string", + "example": "Share of API requests that returned a 5xx response." + }, "id": { "description": "Unique tile ID assigned by the server.", "type": "string", @@ -34609,6 +35231,11 @@ "type": "string", "example": "errors" }, + "description": { + "description": "Short explanation of the tile, shown as a tooltip next to its name.", + "type": "string", + "example": "Share of API requests that returned a 5xx response." + }, "id": { "description": "Optional tile ID. Omit to generate a new ID.", "type": "string", @@ -34637,10 +35264,10 @@ "h" ] }, - "ClickStackFilterInput": { + "ClickStackQueryExpressionFilterInput": { "properties": { "type": { - "description": "Filter type. Must be \"QUERY_EXPRESSION\".", + "description": "Filter type discriminator. Must be \"QUERY_EXPRESSION\".", "type": "string", "enum": [ "QUERY_EXPRESSION" @@ -34712,6 +35339,28 @@ "description": "Token tiles reference this filter's selected value by, as `$variableName`. Must start with a letter and may contain only letters, numbers, and underscores. Defaults to the display name with whitespace replaced by underscores and remaining illegal characters removed, so a variable-enabled filter whose name derives nothing usable must send this field explicitly. Variable names must be unique across a dashboard's variable-enabled filters. Names the variable only, so the field is rejected when isVariableEnabled is not true, and is omitted from responses for such a filter.", "type": "string", "example": "environment" + }, + "minSelections": { + "description": "Minimum number of values that must be selected before tiles load. Set to 1 to make the filter required. Only 0 and 1 are accepted. Omit the field (or send 0) for the default optional behavior.", + "type": "integer", + "enum": [ + 0, + 1 + ], + "example": 1 + }, + "isGlobalRequirement": { + "description": "Widens a required filter's block to every tile on the dashboard. False (the default) blocks only the tiles that read the filter: those referencing its variableName, and those its broadcast applies to. Ignored unless minSelections is 1.", + "type": "boolean", + "example": true + }, + "maxSelections": { + "description": "Maximum number of values that can be selected at once. Set to 1 to make the filter single-select. Omit the field for the default multi-select behavior.", + "type": "integer", + "enum": [ + 1 + ], + "example": 1 } }, "required": [ @@ -34721,60 +35370,180 @@ "sourceId" ] }, - "ClickStackFilter": { + "ClickStackStaticListFilterInput": { "properties": { "type": { - "description": "Filter type. Must be \"QUERY_EXPRESSION\".", + "description": "Discriminator. Must be \"STATIC_LIST\".", "type": "string", "enum": [ - "QUERY_EXPRESSION" + "STATIC_LIST" ], - "example": "QUERY_EXPRESSION" + "example": "STATIC_LIST" }, "name": { "description": "Display name for the dashboard filter key", "type": "string", "example": "Environment" }, - "expression": { - "description": "SQL expression used when querying values for this filter, and when applying this dashboard filter to tiles.", + "options": { + "type": "array", + "description": "The values this filter's dropdown offers. Must be non-empty and free of duplicates.", + "items": { + "type": "string" + }, + "example": [ + "prod", + "staging", + "dev" + ] + }, + "isBroadcastEnabled": { + "description": "Must be false — there is no expression to broadcast. Omit it and it is false. Allowed values: false.", + "type": "boolean", + "example": false + }, + "isVariableEnabled": { + "description": "Must be true if provided, and is true when omitted. Tiles reference the selection as `$variableName`, or with the `$__filter(, $)` and `$__conditionalAll(, $)` macros. The one-argument `$__filter($)` form renders the filter's own expression, which this kind of filter does not have, so it reports that the expression must be passed explicitly. Allowed values: true.", + "type": "boolean", + "example": true + }, + "variableName": { + "description": "Token tiles reference this filter's selected value by, as `$variableName`. Must start with a letter and may contain only letters, numbers, and underscores. Defaults to the display name with whitespace replaced by underscores and remaining illegal characters removed, so a variable-enabled filter whose name derives nothing usable must send this field explicitly. Variable names must be unique across a dashboard's variable-enabled filters. Names the variable only, so the field is rejected when isVariableEnabled is not true, and is omitted from responses for such a filter.", "type": "string", "example": "environment" }, + "minSelections": { + "description": "Minimum number of values that must be selected before tiles load. Set to 1 to make the filter required. Only 0 and 1 are accepted. Omit the field (or send 0) for the default optional behavior.", + "type": "integer", + "enum": [ + 0, + 1 + ], + "example": 1 + }, + "isGlobalRequirement": { + "description": "Widens a required filter's block to every tile on the dashboard. False (the default) blocks only the tiles that read the filter: those referencing its variableName, and those its broadcast applies to. Ignored unless minSelections is 1.", + "type": "boolean", + "example": true + }, + "maxSelections": { + "description": "Maximum number of values that can be selected at once. Set to 1 to make the filter single-select. Omit the field for the default multi-select behavior.", + "type": "integer", + "enum": [ + 1 + ], + "example": 1 + } + }, + "required": [ + "type", + "name", + "options" + ] + }, + "ClickStackPrometheusLabelFilterInput": { + "properties": { + "type": { + "description": "Discriminator. Must be \"PROMETHEUS_LABEL\".", + "type": "string", + "enum": [ + "PROMETHEUS_LABEL" + ], + "example": "PROMETHEUS_LABEL" + }, + "name": { + "description": "Display name for the dashboard filter", + "type": "string", + "example": "Pod" + }, "sourceId": { - "description": "Source ID this dashboard filter key applies to", + "description": "Id of the PromQL source the label values are read from.", "type": "string", - "example": "65f5e4a3b9e77c001a111111" + "example": "65f5e4a3b9e77c001a123456" }, - "sourceMetricType": { - "description": "Metric type when source is metrics", + "label": { + "description": "Label whose values populate the dropdown. Use \"__name__\" to list metric names.", + "type": "string", + "example": "pod" + }, + "match": { + "description": "Optional Prometheus series selector narrowing which series the label values are read from.", + "type": "string", + "example": "up{job=\"api\"}" + }, + "isBroadcastEnabled": { + "description": "Must be false or omitted. Allowed values: false.", + "type": "boolean", + "example": false + }, + "isVariableEnabled": { + "description": "Must be true if provided, and is true when omitted. Tiles reference the selection as `$variableName`. Allowed values: true.", + "type": "boolean", + "example": true + }, + "variableName": { + "description": "Token that tiles reference this filter's selected value by, as `$variableName`. Must start with a letter and may contain only letters, numbers, and underscores. Defaults to the display name with whitespace replaced by underscores and remaining illegal characters removed. Variable names must be unique across a dashboard's variable-enabled filters.", "type": "string", + "example": "pod" + }, + "minSelections": { + "description": "Minimum number of values that must be selected before tiles load. Set to 1 to make the filter required. Only 0 and 1 are accepted. Omit the field (or send 0) for the default optional behavior.", + "type": "integer", "enum": [ - "sum", - "gauge", - "histogram", - "summary", - "exponential histogram" + 0, + 1 ], - "example": "gauge" + "example": 1 }, - "where": { - "description": "Optional WHERE condition to scope which rows this filter key reads values from", - "type": "string", - "example": "ServiceName:api" + "isGlobalRequirement": { + "description": "Widens a required filter's block to every tile on the dashboard. False (the default) blocks only the tiles that read the filter: those referencing its variableName, and those its broadcast applies to. Ignored unless minSelections is 1.", + "type": "boolean", + "example": true }, - "whereLanguage": { - "description": "Language of the where condition", + "maxSelections": { + "description": "Maximum number of values that can be selected at once. Set to 1 to make the filter single-select. Omit the field for the default multi-select behavior.", + "type": "integer", + "enum": [ + 1 + ], + "example": 1 + } + }, + "required": [ + "type", + "name", + "sourceId", + "label" + ] + }, + "ClickStackSourceFilterInput": { + "properties": { + "type": { + "description": "Discriminator. Must be \"SOURCE_FILTER\".", "type": "string", "enum": [ - "sql", - "lucene" + "SOURCE_FILTER" ], - "example": "lucene" + "example": "SOURCE_FILTER" + }, + "name": { + "description": "Display name for the dashboard filter", + "type": "string", + "example": "Cluster" + }, + "sourceId": { + "description": "Id of the source whose filterSettings declares this column. Read filterSettings from GET /api/v2/sources to see which columns a source declares.", + "type": "string", + "example": "65f5e4a3b9e77c001a111111" + }, + "expression": { + "description": "Column this filter selects values for. Must match one of the source's filterSettings.columns[].name values, and is the expression the selection is broadcast on.", + "type": "string", + "example": "cluster" }, "appliesToSourceIds": { "type": "array", - "description": "Optional list of source IDs this filter applies to. Omit or provide an empty array to apply the filter to ALL tiles regardless of source. A non-empty array restricts the filter to only tiles whose source ID is in the list; tiles using other sources are not affected by the selected filter value(s). Scopes the broadcast condition only, so a non-empty array is rejected when isBroadcastEnabled is false, and is omitted from responses for such a filter.", + "description": "Optional list of source IDs this filter broadcasts to. Omit or provide an empty array to apply the selection to ALL tiles regardless of source; a non-empty array restricts it to tiles whose source ID is in the list.", "items": { "type": "string" }, @@ -34783,31 +35552,157 @@ ] }, "isBroadcastEnabled": { - "description": "Whether the selected value is applied as a filter condition on every builder tile this filter applies to (see appliesToSourceIds), and every raw sql tile using the $__filters macro. Omitting the field means enabled.", + "description": "Must be true if provided, and is true when omitted — a source filter should always broadcast to at least its own source. Allowed values: true.", "type": "boolean", - "example": false + "example": true }, "isVariableEnabled": { - "description": "Whether the selected value is exposed to tile queries as a dashboard variable named by variableName. Tiles may reference it as `$variableName` or using the (preferred) `$__filter($)` and `$__conditionalAll(, $)` macros.", + "description": "Must be true if provided, and is true when omitted. Tiles reference the selection as `$variableName`. Allowed values: true.", "type": "boolean", "example": true }, "variableName": { - "description": "Token tiles reference this filter's selected value by, as `$variableName`. Must start with a letter and may contain only letters, numbers, and underscores. Defaults to the display name with whitespace replaced by underscores and remaining illegal characters removed, so a variable-enabled filter whose name derives nothing usable must send this field explicitly. Variable names must be unique across a dashboard's variable-enabled filters. Names the variable only, so the field is rejected when isVariableEnabled is not true, and is omitted from responses for such a filter.", + "description": "Token that tiles reference this filter's selected value by, as `$variableName`. Must start with a letter and may contain only letters, numbers, and underscores. Defaults to the display name with whitespace replaced by underscores and remaining illegal characters removed. Variable names must be unique across a dashboard's variable-enabled filters.", "type": "string", - "example": "environment" + "example": "cluster" }, - "id": { - "description": "Unique dashboard filter key ID", - "type": "string" + "minSelections": { + "description": "Minimum number of values that must be selected before tiles load. Set to 1 to make the filter required. Only 0 and 1 are accepted. Omit the field (or send 0) for the default optional behavior.", + "type": "integer", + "enum": [ + 0, + 1 + ], + "example": 1 + }, + "isGlobalRequirement": { + "description": "Widens a required filter's block to every tile on the dashboard. False (the default) blocks only the tiles that read the filter: those referencing its variableName, and those its broadcast applies to. Ignored unless minSelections is 1.", + "type": "boolean", + "example": true + }, + "maxSelections": { + "description": "Maximum number of values that can be selected at once. Set to 1 to make the filter single-select. Omit the field for the default multi-select behavior.", + "type": "integer", + "enum": [ + 1 + ], + "example": 1 } }, "required": [ "type", "name", - "expression", "sourceId", - "id" + "expression", + "variableName" + ] + }, + "ClickStackFilterInput": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackQueryExpressionFilterInput" + }, + { + "$ref": "#/components/schemas/ClickStackStaticListFilterInput" + }, + { + "$ref": "#/components/schemas/ClickStackPrometheusLabelFilterInput" + }, + { + "$ref": "#/components/schemas/ClickStackSourceFilterInput" + } + ] + }, + "ClickStackQueryExpressionFilter": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackQueryExpressionFilterInput" + }, + { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "description": "Unique dashboard filter key ID" + } + } + } + ] + }, + "ClickStackStaticListFilter": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackStaticListFilterInput" + }, + { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "description": "Unique dashboard filter key ID" + } + } + } + ] + }, + "ClickStackPrometheusLabelFilter": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackPrometheusLabelFilterInput" + }, + { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "description": "Unique dashboard filter key ID" + } + } + } + ] + }, + "ClickStackSourceFilter": { + "allOf": [ + { + "$ref": "#/components/schemas/ClickStackSourceFilterInput" + }, + { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "description": "Unique dashboard filter key ID" + } + } + } + ] + }, + "ClickStackFilter": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackQueryExpressionFilter" + }, + { + "$ref": "#/components/schemas/ClickStackStaticListFilter" + }, + { + "$ref": "#/components/schemas/ClickStackPrometheusLabelFilter" + }, + { + "$ref": "#/components/schemas/ClickStackSourceFilter" + } ] }, "ClickStackCreateDashboardRequest": { @@ -34881,80 +35776,157 @@ "tiles" ] }, - "ClickStackValidateDashboardError": { + "ClickStackValidateDashboardError": { + "properties": { + "path": { + "description": "Dot-separated field path, or empty string for top-level errors.", + "type": "string", + "example": "tiles.0.config" + }, + "message": { + "description": "Human-readable error description.", + "type": "string", + "example": "Required" + } + }, + "required": [ + "path", + "message" + ] + }, + "ClickStackValidateDashboardResponseNormalized": { + "properties": {} + }, + "ClickStackValidateDashboardResponse": { + "properties": { + "valid": { + "description": "True when the body passes all validation rules.", + "type": "boolean" + }, + "errors": { + "type": "array", + "description": "Validation errors. Empty when valid is true.", + "items": { + "$ref": "#/components/schemas/ClickStackValidateDashboardError" + } + }, + "normalized": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackValidateDashboardResponseNormalized" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "valid", + "errors", + "normalized" + ] + }, + "ClickStackUpdateDashboardRequest": { + "properties": { + "name": { + "description": "Dashboard name.", + "type": "string", + "example": "Updated Dashboard Name" + }, + "tiles": { + "type": "array", + "description": "Full list of tiles for the dashboard. Existing tiles are matched by ID; tiles with an ID that does not match an existing tile will be assigned a new generated ID.", + "items": { + "$ref": "#/components/schemas/ClickStackTileInput" + } + }, + "tags": { + "type": "array", + "description": "Tags for organizing and filtering dashboards.", + "items": { + "type": "string" + }, + "example": [ + "production", + "updated" + ] + }, + "filters": { + "type": "array", + "description": "Dropdown filters added to the dashboard. Each one broadcasts its selected value as a condition, acts as a variable which can be referenced in tile queries, or both.", + "items": { + "$ref": "#/components/schemas/ClickStackFilter" + } + }, + "savedQuery": { + "description": "Optional default dashboard query to persist on the dashboard.", + "type": [ + "string", + "null" + ], + "example": "service.name = 'api'" + }, + "savedQueryLanguage": { + "description": "Query language used by savedQuery.", + "type": [ + "string", + "null" + ], + "enum": [ + "sql", + "lucene" + ], + "example": "sql" + }, + "savedFilterValues": { + "type": "array", + "description": "Optional default dashboard filter values to persist on the dashboard.", + "items": { + "$ref": "#/components/schemas/ClickStackSavedFilterValue" + } + }, + "containers": { + "type": "array", + "description": "Optional grouping containers. Each tile may join a container via tile.containerId, and a tab inside it via tile.tabId.", + "items": { + "$ref": "#/components/schemas/ClickStackDashboardContainer" + } + } + }, + "required": [ + "name", + "tiles" + ] + }, + "ClickStackDashboardResponse": { "properties": { - "path": { - "description": "Dot-separated field path, or empty string for top-level errors.", - "type": "string", - "example": "tiles.0.config" - }, - "message": { - "description": "Human-readable error description.", + "id": { + "description": "Dashboard ID", "type": "string", - "example": "Required" - } - }, - "required": [ - "path", - "message" - ] - }, - "ClickStackValidateDashboardResponseNormalized": { - "properties": {} - }, - "ClickStackValidateDashboardResponse": { - "properties": { - "valid": { - "description": "True when the body passes all validation rules.", - "type": "boolean" - }, - "errors": { - "type": "array", - "description": "Validation errors. Empty when valid is true.", - "items": { - "$ref": "#/components/schemas/ClickStackValidateDashboardError" - } + "example": "65f5e4a3b9e77c001a567890" }, - "normalized": { - "oneOf": [ - { - "$ref": "#/components/schemas/ClickStackValidateDashboardResponseNormalized" - }, - { - "type": "null" - } - ] - } - }, - "required": [ - "valid", - "errors", - "normalized" - ] - }, - "ClickStackUpdateDashboardRequest": { - "properties": { "name": { - "description": "Dashboard name.", + "description": "Dashboard name", "type": "string", - "example": "Updated Dashboard Name" + "example": "Service Overview" }, "tiles": { "type": "array", - "description": "Full list of tiles for the dashboard. Existing tiles are matched by ID; tiles with an ID that does not match an existing tile will be assigned a new generated ID.", + "description": "List of tiles/charts in the dashboard", "items": { - "$ref": "#/components/schemas/ClickStackTileInput" + "$ref": "#/components/schemas/ClickStackTileOutput" } }, "tags": { "type": "array", - "description": "Tags for organizing and filtering dashboards.", + "description": "Tags for organizing and filtering dashboards", "items": { "type": "string" }, "example": [ "production", - "updated" + "monitoring" ] }, "filters": { @@ -34965,7 +35937,7 @@ } }, "savedQuery": { - "description": "Optional default dashboard query to persist on the dashboard.", + "description": "Optional default dashboard query restored when loading the dashboard.", "type": [ "string", "null" @@ -34986,7 +35958,7 @@ }, "savedFilterValues": { "type": "array", - "description": "Optional default dashboard filter values to persist on the dashboard.", + "description": "Optional default dashboard filter values restored when loading the dashboard.", "items": { "$ref": "#/components/schemas/ClickStackSavedFilterValue" } @@ -34998,100 +35970,185 @@ "$ref": "#/components/schemas/ClickStackDashboardContainer" } } - }, - "required": [ - "name", - "tiles" - ] + } }, - "ClickStackDashboardResponse": { + "ClickStackMongoDBCASLPermissionConditions": { + "type": "object", + "properties": {}, + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "$regex" + ], + "properties": { + "$regex": { + "type": "string" + }, + "$options": { + "type": "string" + } + } + } + ] + } + }, + "ClickStackMongoDBCASLPermission": { "properties": { - "id": { - "description": "Dashboard ID", + "action": { + "description": "The action this permission grants or denies.", "type": "string", - "example": "65f5e4a3b9e77c001a567890" + "enum": [ + "create", + "read", + "update", + "delete", + "manage" + ], + "example": "read" }, - "name": { - "description": "Dashboard name", + "subject": { + "description": "The resource the action applies to.", "type": "string", - "example": "Service Overview" - }, - "tiles": { - "type": "array", - "description": "List of tiles/charts in the dashboard", - "items": { - "$ref": "#/components/schemas/ClickStackTileOutput" - } + "enum": [ + "Alert", + "Connection", + "Dashboard", + "SavedSearch", + "Source", + "Team", + "User", + "Webhook", + "Role", + "Notebook", + "SchemaMigration", + "all" + ], + "example": "Dashboard" }, - "tags": { + "fields": { "type": "array", - "description": "Tags for organizing and filtering dashboards", + "description": "Optional CASL field restriction limiting the rule to specific fields.", "items": { "type": "string" - }, - "example": [ - "production", - "monitoring" - ] - }, - "filters": { - "type": "array", - "description": "Dropdown filters added to the dashboard. Each one broadcasts its selected value as a condition, acts as a variable which can be referenced in tile queries, or both.", - "items": { - "$ref": "#/components/schemas/ClickStackFilter" } }, - "savedQuery": { - "description": "Optional default dashboard query restored when loading the dashboard.", - "type": [ - "string", - "null" - ], - "example": "service.name = 'api'" + "inverted": { + "description": "When true, the rule denies rather than grants the action.", + "type": "boolean", + "example": false }, - "savedQueryLanguage": { - "description": "Query language used by savedQuery.", - "type": [ - "string", - "null" + "integration": { + "description": "Scopes the permission to the application (MongoDB) data plane.", + "type": "string", + "enum": [ + "mongodb" ], + "example": "mongodb" + }, + "conditions": { + "$ref": "#/components/schemas/ClickStackMongoDBCASLPermissionConditions" + } + }, + "required": [ + "action", + "subject", + "integration" + ] + }, + "ClickStackClickHouseCASLPermissionConditionsFrom": { + "properties": { + "databaseName": { + "description": "", + "type": "string", + "example": "default" + }, + "tableName": { + "description": "", + "type": "string", + "example": "otel_logs" + } + }, + "required": [ + "databaseName", + "tableName" + ] + }, + "ClickStackClickHouseCASLPermissionConditions": { + "properties": { + "connection": { + "description": "ID of the connection the row policy applies to.", + "type": "string", + "example": "507f1f77bcf86cd799439011" + }, + "from": { + "$ref": "#/components/schemas/ClickStackClickHouseCASLPermissionConditionsFrom" + }, + "where": { + "description": "The row-policy predicate, expressed in `whereLanguage`.", + "type": "string", + "example": "ServiceName = 'checkout'" + }, + "whereLanguage": { + "description": "", + "type": "string", "enum": [ "sql", "lucene" ], "example": "sql" - }, - "savedFilterValues": { - "type": "array", - "description": "Optional default dashboard filter values restored when loading the dashboard.", - "items": { - "$ref": "#/components/schemas/ClickStackSavedFilterValue" - } - }, - "containers": { - "type": "array", - "description": "Optional grouping containers. Each tile may join a container via tile.containerId, and a tab inside it via tile.tabId.", - "items": { - "$ref": "#/components/schemas/ClickStackDashboardContainer" - } } - } - }, - "ClickStackCASLPermissionConditions": { - "type": "object", - "additionalProperties": true + }, + "required": [ + "connection", + "from", + "where", + "whereLanguage" + ] }, - "ClickStackCASLPermission": { + "ClickStackClickHouseCASLPermission": { "properties": { "action": { "description": "The action this permission grants or denies.", "type": "string", + "enum": [ + "create", + "read", + "update", + "delete", + "manage" + ], "example": "read" }, "subject": { "description": "The resource the action applies to.", "type": "string", - "example": "dashboard" + "enum": [ + "Alert", + "Connection", + "Dashboard", + "SavedSearch", + "Source", + "Team", + "User", + "Webhook", + "Role", + "Notebook", + "SchemaMigration", + "all" + ], + "example": "Dashboard" + }, + "fields": { + "type": "array", + "description": "Optional CASL field restriction limiting the rule to specific fields.", + "items": { + "type": "string" + } }, "inverted": { "description": "When true, the rule denies rather than grants the action.", @@ -35099,17 +36156,31 @@ "example": false }, "integration": { - "description": "The integration the permission is scoped to.", + "description": "Scopes the permission to the ClickHouse data plane (row-level access).", "type": "string", - "example": "mongodb" + "enum": [ + "clickhouse" + ], + "example": "clickhouse" }, "conditions": { - "$ref": "#/components/schemas/ClickStackCASLPermissionConditions" + "$ref": "#/components/schemas/ClickStackClickHouseCASLPermissionConditions" } }, "required": [ "action", - "subject" + "subject", + "integration" + ] + }, + "ClickStackCASLPermission": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackMongoDBCASLPermission" + }, + { + "$ref": "#/components/schemas/ClickStackClickHouseCASLPermission" + } ] }, "ClickStackRole": { @@ -35210,29 +36281,6 @@ "permissions" ] }, - "ClickStackValidationErrorItemErrors": { - "type": "object", - "additionalProperties": true - }, - "ClickStackValidationErrorItem": { - "properties": { - "type": { - "description": "Request part that failed validation.", - "type": "string", - "example": "Body" - }, - "errors": { - "$ref": "#/components/schemas/ClickStackValidationErrorItemErrors" - } - } - }, - "ClickStackValidationError": { - "type": "array", - "description": "zod-express-middleware validation failures.", - "items": { - "$ref": "#/components/schemas/ClickStackValidationErrorItem" - } - }, "ClickStackSavedSearchFilter": { "properties": { "type": { @@ -35794,6 +36842,11 @@ ], "example": "TimestampTime" }, + "orderByExpression": { + "description": "Custom ORDER BY expression that overrides the default ordering. Leave empty to use the auto-detected default.", + "type": "string", + "example": "Timestamp DESC" + }, "metricSourceId": { "description": "HyperDX Source for metrics associated with logs. Optional", "type": [ @@ -36073,6 +37126,11 @@ ], "example": "ResourceAttributes['service.version']" }, + "sampleRateExpression": { + "description": "Column or expression for upstream sampling weight (1/N). When set, aggregations (count, avg, sum, quantile) are corrected for sampling. Percentiles use quantileTDigestWeighted, which is an approximation. Leave empty if spans are not sampled.", + "type": "string", + "example": "SampleRate" + }, "resourceAttributesExpression": { "description": "Expression to extract resource-level attributes.", "type": [ @@ -36097,6 +37155,11 @@ ], "example": "Events" }, + "spanLinksValueExpression": { + "description": "Expression to extract span links. Used to capture links from a span to spans in other traces. Expected to be Nested ( TraceId String, SpanId String, TraceState String, Attributes Map(LowCardinality(String), String) )", + "type": "string", + "example": "Links" + }, "implicitColumnExpression": { "description": "Column used for full text search if no property is specified in a Lucene-based search. Typically the message body of a log.", "type": [ @@ -36140,6 +37203,16 @@ "$ref": "#/components/schemas/ClickStackHighlightedAttributeExpression" } }, + "displayedTimestampValueExpression": { + "description": "This DateTime column is used to display and order search results.", + "type": "string", + "example": "Timestamp" + }, + "orderByExpression": { + "description": "Custom ORDER BY expression that overrides the default ordering. Leave empty to use the auto-detected default.", + "type": "string", + "example": "Timestamp DESC" + }, "materializedViews": { "type": "array", "description": "Configure materialized views for query optimization. These pre-aggregated views can significantly improve query performance on aggregation queries.", @@ -36314,6 +37387,11 @@ "description": "HyperDX Source for traces associated with sessions.", "type": "string", "example": "507f1f77bcf86cd799439021" + }, + "resourceAttributesExpression": { + "description": "Expression to extract resource-level attributes.", + "type": "string", + "example": "ResourceAttributes" } }, "required": [ @@ -36686,14 +37764,18 @@ }, "ClickStackWebhookInputHeaders": { "type": "object", + "properties": {}, "additionalProperties": { - "type": "string" + "type": "string", + "maxLength": 4096 } }, "ClickStackWebhookInputQueryParams": { "type": "object", + "properties": {}, "additionalProperties": { - "type": "string" + "type": "string", + "maxLength": 4096 } }, "ClickStackWebhookInput": { @@ -36724,7 +37806,7 @@ "example": "Sends critical alerts to the #incidents channel" }, "body": { - "description": "Optional request body template. Only for generic/incidentio; rejected for slack.", + "description": "Optional Handlebars request body template. Only for generic/incidentio; rejected for slack. Rejected with a 400 if it does not compile or calls a helper other than `eq` and the Handlebars built-ins.", "type": "string", "example": "{\"alert\": \"{{title}}\", \"severity\": \"{{level}}\"}" }, @@ -36741,6 +37823,55 @@ "url" ] }, + "ClickStackValidateWebhookResponseErrorsItem": { + "properties": { + "path": { + "description": "Dot-separated field path, or empty string for top-level errors.", + "type": "string" + }, + "message": { + "description": "Human-readable error description.", + "type": "string" + } + }, + "required": [ + "path", + "message" + ] + }, + "ClickStackValidateWebhookResponseNormalized": { + "properties": {} + }, + "ClickStackValidateWebhookResponse": { + "properties": { + "valid": { + "description": "True when the body passes all validation rules.", + "type": "boolean" + }, + "errors": { + "type": "array", + "description": "Validation errors. Empty when valid is true.", + "items": { + "$ref": "#/components/schemas/ClickStackValidateWebhookResponseErrorsItem" + } + }, + "normalized": { + "oneOf": [ + { + "$ref": "#/components/schemas/ClickStackValidateWebhookResponseNormalized" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "valid", + "errors", + "normalized" + ] + }, "AssignedRole": { "properties": { "roleId": { @@ -38848,6 +39979,61 @@ } } }, + "PostgresIpAccessListEntry": { + "properties": { + "id": { + "description": "Rule ID returned by the service. Include it when editing an existing rule; omit it when creating a rule.", + "type": "string" + }, + "source": { + "description": "IPv4 address or CIDR allowed to connect. Returned as a canonical CIDR.", + "type": "string", + "example": "203.0.113.0/24" + }, + "portRange": { + "description": "Port or inclusive range, from 1 to 65535. Use \"5432\" for Postgres, \"6432\" for PgBouncer, or a range such as \"5432..6432\".", + "type": "string", + "example": "5432" + }, + "description": { + "description": "Optional description of the access list entry.", + "type": "string" + } + }, + "required": [ + "source", + "portRange" + ] + }, + "PostgresIpAccessListRuleReference": { + "properties": { + "id": { + "description": "Rule ID returned by the service.", + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "PostgresIpAccessListPatch": { + "properties": { + "add": { + "type": "array", + "description": "Rules to create or edit after removals. Omit id to create a TCP rule, or include the returned id to edit an existing rule. Updates can partially succeed; read the current list before retrying.", + "items": { + "$ref": "#/components/schemas/PostgresIpAccessListEntry" + } + }, + "remove": { + "type": "array", + "description": "Rules to remove by their returned IDs before creating or editing rules.", + "items": { + "$ref": "#/components/schemas/PostgresIpAccessListRuleReference" + } + } + } + }, "pgConfig": { "type": "object", "title": "Postgres Configuration", @@ -38892,6 +40078,18 @@ "TLSv1.3" ] }, + "default_toast_compression": { + "type": "string", + "description": "Sets the default compression method for compressible values stored out of line (TOAST).", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/default_toast_compression/" + }, + "example": "lz4", + "enum": [ + "pglz", + "lz4" + ] + }, "maintenance_work_mem": { "type": [ "string", @@ -39013,6 +40211,134 @@ "minimum": 25, "maximum": 75 }, + "temp_buffers": { + "type": [ + "string", + "integer" + ], + "description": "Sets the maximum memory each session uses for temporary table buffers.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/temp_buffers/" + }, + "example": "8MB", + "minimum": 800 + }, + "hash_mem_multiplier": { + "type": [ + "string", + "integer" + ], + "description": "Multiple of work_mem that hash-based operations may use.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/hash_mem_multiplier/" + }, + "example": 2, + "minimum": 1, + "maximum": 1000 + }, + "enable_parallel_hash": { + "type": "string", + "description": "Enables the planner's use of parallel hash plans.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/enable_parallel_hash/" + }, + "example": "on", + "enum": [ + "on", + "off" + ] + }, + "maintenance_io_concurrency": { + "type": [ + "string", + "integer" + ], + "description": "Number of concurrent disk I/O operations used for maintenance work such as vacuum.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/maintenance_io_concurrency/" + }, + "example": 10, + "minimum": 0, + "maximum": 1000 + }, + "io_method": { + "type": "string", + "description": "Selects the method used to perform asynchronous I/O.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/io_method/" + }, + "example": "worker", + "enum": [ + "sync", + "worker", + "io_uring" + ] + }, + "io_workers": { + "type": [ + "string", + "integer" + ], + "description": "Number of I/O worker processes used when io_method is worker.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/io_workers/" + }, + "example": 3, + "minimum": 1, + "maximum": 32 + }, + "bgwriter_delay": { + "type": [ + "string", + "integer" + ], + "description": "Sleep time between background writer activity rounds.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/bgwriter_delay/" + }, + "example": "200ms", + "minimum": 10, + "maximum": 10000 + }, + "bgwriter_flush_after": { + "type": [ + "string", + "integer" + ], + "description": "Amount of data written by the background writer after which the OS is asked to flush it to disk.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/bgwriter_flush_after/" + }, + "example": "512kB", + "minimum": 0, + "maximum": 2048 + }, + "bgwriter_lru_maxpages": { + "type": [ + "string", + "integer" + ], + "description": "Maximum number of buffers the background writer writes per round.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/bgwriter_lru_maxpages/" + }, + "example": 100, + "minimum": 0, + "maximum": 1073741823 + }, + "bgwriter_lru_multiplier": { + "type": [ + "string", + "integer" + ], + "description": "Multiple of the average recent buffer demand that the background writer cleans per round.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/bgwriter_lru_multiplier/" + }, + "example": 2, + "minimum": 0, + "maximum": 10 + }, "statement_timeout": { "type": [ "string", @@ -39173,6 +40499,70 @@ "minimum": 32, "maximum": 262144 }, + "max_replication_slots": { + "type": [ + "string", + "integer" + ], + "description": "Maximum number of replication slots. Standbys and logical replication each use slots.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/max_replication_slots/" + }, + "example": 10, + "minimum": 10, + "maximum": 262143 + }, + "max_wal_senders": { + "type": [ + "string", + "integer" + ], + "description": "Maximum number of simultaneously running WAL sender processes.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/max_wal_senders/" + }, + "example": 10, + "minimum": 10, + "maximum": 262143 + }, + "max_logical_replication_workers": { + "type": [ + "string", + "integer" + ], + "description": "Maximum number of logical replication workers for subscriptions on this server.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/max_logical_replication_workers/" + }, + "example": 4, + "minimum": 0, + "maximum": 262143 + }, + "max_sync_workers_per_subscription": { + "type": [ + "string", + "integer" + ], + "description": "Maximum number of table synchronization workers per subscription.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/max_sync_workers_per_subscription/" + }, + "example": 2, + "minimum": 0, + "maximum": 262143 + }, + "logical_decoding_work_mem": { + "type": [ + "string", + "integer" + ], + "description": "Maximum memory used for logical decoding before changes spill to disk.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/logical_decoding_work_mem/" + }, + "example": "64MB", + "minimum": 64 + }, "autovacuum_max_workers": { "type": [ "string", @@ -39320,6 +40710,83 @@ }, "example": "2ms", "minimum": 0 + }, + "vacuum_cost_delay": { + "type": [ + "string", + "integer" + ], + "description": "Time in milliseconds a manual VACUUM sleeps when its cost limit is exceeded. 0 disables cost-based delay.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/vacuum_cost_delay/" + }, + "example": "0", + "minimum": 0, + "maximum": 100 + }, + "log_min_duration_statement": { + "type": [ + "string", + "integer" + ], + "description": "Logs statements that run at least this many milliseconds. 0 logs all statements; -1 disables.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/log_min_duration_statement/" + }, + "example": 1000, + "minimum": -1, + "maximum": 2147483647 + }, + "log_temp_files": { + "type": [ + "string", + "integer" + ], + "description": "Logs temporary files of at least this size in kilobytes. 0 logs all temporary files; -1 disables.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/log_temp_files/" + }, + "example": "-1", + "minimum": -1, + "maximum": 2147483647 + }, + "log_lock_waits": { + "type": "string", + "description": "Logs a message when a session waits longer than deadlock_timeout to acquire a lock.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/log_lock_waits/" + }, + "example": "off", + "enum": [ + "on", + "off" + ] + }, + "log_parameter_max_length": { + "type": [ + "string", + "integer" + ], + "description": "Maximum bytes of each bind parameter value logged with statements. 0 disables; -1 logs values in full.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/log_parameter_max_length/" + }, + "example": "-1", + "minimum": -1, + "maximum": 1073741823 + }, + "track_activity_query_size": { + "type": [ + "string", + "integer" + ], + "description": "Bytes reserved to store the text of the currently running command of each session, shown in pg_stat_activity.", + "externalDocs": { + "url": "https://postgresqlco.nf/doc/en/param/track_activity_query_size/" + }, + "example": 4096, + "minimum": 100, + "maximum": 1048576 } }, "additionalProperties": false, @@ -39434,67 +40901,67 @@ "r8gd.16xlarge", "r8gd.24xlarge", "r8gd.48xlarge", - "c4a-highmem-4", - "c4a-highmem-8", - "c4a-highmem-16", - "c4a-highmem-32", - "c4a-highmem-48", - "c4a-highmem-64", - "c4a-highmem-72", - "c4a-standard-4", - "c4a-standard-8", - "c4a-standard-16", - "c4a-standard-32", - "c4a-standard-48", - "c4a-standard-64", - "c4a-standard-72", - "c4-highmem-4", - "c4-highmem-8", - "c4-highmem-16", - "c4-highmem-24", - "c4-highmem-32", - "c4-highmem-48", - "c4-highmem-96", - "c4-highmem-144", - "c4-highmem-192", - "c4-highmem-288", - "c4-standard-4", - "c4-standard-8", - "c4-standard-16", - "c4-standard-24", - "c4-standard-32", - "c4-standard-48", - "c4-standard-96", - "c4-standard-144", - "c4-standard-192", - "c4-standard-288", - "c4d-highmem-8", - "c4d-highmem-16", - "c4d-highmem-32", - "c4d-highmem-48", - "c4d-highmem-64", - "c4d-highmem-96", - "c4d-highmem-192", - "c4d-highmem-384", - "c4d-standard-8", - "c4d-standard-16", - "c4d-standard-32", - "c4d-standard-48", - "c4d-standard-64", - "c4d-standard-96", - "c4d-standard-192", - "c4d-standard-384", - "z3-highlssd-8", - "z3-highlssd-16", - "z3-highlssd-22", - "z3-highlssd-32", - "z3-highlssd-44", - "z3-highlssd-88", - "z3-standardlssd-14", - "z3-standardlssd-22", - "z3-standardlssd-44", - "z3-standardlssd-88", - "z3-standardlssd-176" + "c4a-highmem-lssd-4", + "c4a-highmem-lssd-8", + "c4a-highmem-lssd-16", + "c4a-highmem-lssd-32", + "c4a-highmem-lssd-48", + "c4a-highmem-lssd-64", + "c4a-highmem-lssd-72", + "c4a-standard-lssd-4", + "c4a-standard-lssd-8", + "c4a-standard-lssd-16", + "c4a-standard-lssd-32", + "c4a-standard-lssd-48", + "c4a-standard-lssd-64", + "c4a-standard-lssd-72", + "c4-highmem-lssd-4", + "c4-highmem-lssd-8", + "c4-highmem-lssd-16", + "c4-highmem-lssd-24", + "c4-highmem-lssd-32", + "c4-highmem-lssd-48", + "c4-highmem-lssd-96", + "c4-highmem-lssd-144", + "c4-highmem-lssd-192", + "c4-highmem-lssd-288", + "c4-standard-lssd-4", + "c4-standard-lssd-8", + "c4-standard-lssd-16", + "c4-standard-lssd-24", + "c4-standard-lssd-32", + "c4-standard-lssd-48", + "c4-standard-lssd-96", + "c4-standard-lssd-144", + "c4-standard-lssd-192", + "c4-standard-lssd-288", + "c4d-highmem-lssd-8", + "c4d-highmem-lssd-16", + "c4d-highmem-lssd-32", + "c4d-highmem-lssd-48", + "c4d-highmem-lssd-64", + "c4d-highmem-lssd-96", + "c4d-highmem-lssd-192", + "c4d-highmem-lssd-384", + "c4d-standard-lssd-8", + "c4d-standard-lssd-16", + "c4d-standard-lssd-32", + "c4d-standard-lssd-48", + "c4d-standard-lssd-64", + "c4d-standard-lssd-96", + "c4d-standard-lssd-192", + "c4d-standard-lssd-384", + "z3-highmem-highlssd-8", + "z3-highmem-highlssd-16", + "z3-highmem-highlssd-22", + "z3-highmem-highlssd-32", + "z3-highmem-highlssd-44", + "z3-highmem-highlssd-88", + "z3-highmem-standardlssd-14", + "z3-highmem-standardlssd-22", + "z3-highmem-standardlssd-44", + "z3-highmem-standardlssd-88", + "z3-highmem-standardlssd-176" ] }, "pgHaType": { @@ -39626,6 +41093,13 @@ "tags": { "$ref": "#/components/schemas/pgTags" }, + "ipAccessList": { + "type": "array", + "description": "IPv4 access rules shown in the console. IPv6 rules are not included. Returned when reading or configuring access; omitted from other service updates.", + "items": { + "$ref": "#/components/schemas/PostgresIpAccessListEntry" + } + }, "id": { "$ref": "#/components/schemas/pgIdProperty" }, @@ -39682,6 +41156,13 @@ "tags": { "$ref": "#/components/schemas/pgTags" }, + "ipAccessList": { + "type": "array", + "description": "IPv4 TCP access rules to apply when creating the service. Omit to allow all IPv4 and IPv6 addresses on Postgres (5432) and PgBouncer (6432). Provide an empty list to block public access. When provided, only these rules are allowed; include separate rules for each required port.", + "items": { + "$ref": "#/components/schemas/PostgresIpAccessListEntry" + } + }, "pgConfig": { "$ref": "#/components/schemas/pgConfig" }, @@ -39698,6 +41179,9 @@ }, "PostgresServicePatchRequest": { "properties": { + "ipAccessList": { + "$ref": "#/components/schemas/PostgresIpAccessListPatch" + }, "name": { "$ref": "#/components/schemas/pgNameProperty" }, @@ -41980,6 +43464,7 @@ }, "functionName": { "type": "string", + "maxLength": 64, "pattern": "^[A-Za-z][A-Za-z0-9_]*$" } }, @@ -42089,6 +43574,7 @@ }, "functionName": { "type": "string", + "maxLength": 64, "pattern": "^[A-Za-z][A-Za-z0-9_]*$" } }, diff --git a/crates/clickhouse-cloud-api/src/convert/postgres.rs b/crates/clickhouse-cloud-api/src/convert/postgres.rs index 2a091625d..8882941c8 100644 --- a/crates/clickhouse-cloud-api/src/convert/postgres.rs +++ b/crates/clickhouse-cloud-api/src/convert/postgres.rs @@ -18,19 +18,38 @@ impl From for PgConfig { autovacuum_vacuum_scale_factor: value.autovacuum_vacuum_scale_factor, autovacuum_vacuum_threshold: value.autovacuum_vacuum_threshold, autovacuum_work_mem: value.autovacuum_work_mem, + bgwriter_delay: value.bgwriter_delay, + bgwriter_flush_after: value.bgwriter_flush_after, + bgwriter_lru_maxpages: value.bgwriter_lru_maxpages, + bgwriter_lru_multiplier: value.bgwriter_lru_multiplier, checkpoint_timeout: value.checkpoint_timeout, + default_toast_compression: value.default_toast_compression, default_transaction_isolation: value.default_transaction_isolation, effective_cache_size: value.effective_cache_size, effective_io_concurrency: value.effective_io_concurrency, + enable_parallel_hash: value.enable_parallel_hash, + hash_mem_multiplier: value.hash_mem_multiplier, idle_in_transaction_session_timeout: value.idle_in_transaction_session_timeout, idle_session_timeout: value.idle_session_timeout, + io_method: value.io_method, + io_workers: value.io_workers, lock_timeout: value.lock_timeout, + log_lock_waits: value.log_lock_waits, + log_min_duration_statement: value.log_min_duration_statement, + log_parameter_max_length: value.log_parameter_max_length, + log_temp_files: value.log_temp_files, + logical_decoding_work_mem: value.logical_decoding_work_mem, + maintenance_io_concurrency: value.maintenance_io_concurrency, maintenance_work_mem: value.maintenance_work_mem, max_connections: value.max_connections, + max_logical_replication_workers: value.max_logical_replication_workers, max_parallel_maintenance_workers: value.max_parallel_maintenance_workers, max_parallel_workers: value.max_parallel_workers, max_parallel_workers_per_gather: value.max_parallel_workers_per_gather, + max_replication_slots: value.max_replication_slots, max_slot_wal_keep_size: value.max_slot_wal_keep_size, + max_sync_workers_per_subscription: value.max_sync_workers_per_subscription, + max_wal_senders: value.max_wal_senders, max_wal_size: value.max_wal_size, max_worker_processes: value.max_worker_processes, min_wal_size: value.min_wal_size, @@ -38,7 +57,10 @@ impl From for PgConfig { shared_memory_percent: value.shared_memory_percent, ssl_min_protocol_version: value.ssl_min_protocol_version, statement_timeout: value.statement_timeout, + temp_buffers: value.temp_buffers, + track_activity_query_size: value.track_activity_query_size, transaction_timeout: value.transaction_timeout, + vacuum_cost_delay: value.vacuum_cost_delay, wal_buffers: value.wal_buffers, wal_compression: value.wal_compression, wal_keep_size: value.wal_keep_size, @@ -73,3 +95,28 @@ impl TryFrom for PostgresInstanceConfig { } } } + +impl TryFrom for PostgresIpAccessListEntry { + type Error = MissingRequiredFields; + + /// Turns a fetched rule into an `ipAccessList.add` entry that edits it in + /// place. `source` and `portRange` are required on write. + fn try_from(value: PostgresIpAccessListEntryResponse) -> Result { + let mut missing = Vec::new(); + if value.port_range.is_none() { + missing.push("portRange"); + } + if value.source.is_none() { + missing.push("source"); + } + match (value.port_range, value.source) { + (Some(port_range), Some(source)) => Ok(Self { + description: value.description, + id: value.id, + port_range, + source, + }), + _ => Err(MissingRequiredFields::new(missing)), + } + } +} diff --git a/crates/clickhouse-cloud-api/src/meta/operations.rs b/crates/clickhouse-cloud-api/src/meta/operations.rs index be3f40673..dd9675599 100644 --- a/crates/clickhouse-cloud-api/src/meta/operations.rs +++ b/crates/clickhouse-cloud-api/src/meta/operations.rs @@ -534,6 +534,15 @@ pub const CLICK_STACK_VALIDATE_DASHBOARD: OperationMetadata = OperationMetadata required_permissions: &["control-plane:service:manage-clickstack-api"], }; +/// `POST /v1/organizations/{organizationId}/services/{serviceId}/clickstack/webhooks/validate` (`clickStackValidateWebhook`). +pub const CLICK_STACK_VALIDATE_WEBHOOK: OperationMetadata = OperationMetadata { + operation_id: "clickStackValidateWebhook", + rust_method: "click_stack_validate_webhook", + method: "POST", + path: "/v1/organizations/{organizationId}/services/{serviceId}/clickstack/webhooks/validate", + required_permissions: &["control-plane:service:manage-clickstack-api"], +}; + /// `GET /v1/organizations/{organizationId}/creditBalances` (`creditBalancesGet`). pub const CREDIT_BALANCES_GET: OperationMetadata = OperationMetadata { operation_id: "creditBalancesGet", @@ -1631,6 +1640,7 @@ pub const ALL: &[OperationMetadata] = &[ CLICK_STACK_UPDATE_SOURCE, CLICK_STACK_UPDATE_WEBHOOK, CLICK_STACK_VALIDATE_DASHBOARD, + CLICK_STACK_VALIDATE_WEBHOOK, CREDIT_BALANCES_GET, INSTANCE_CREATE, INSTANCE_DELETE, diff --git a/crates/clickhouse-cloud-api/src/models.rs b/crates/clickhouse-cloud-api/src/models.rs index 7beb421c2..59c15b6da 100644 --- a/crates/clickhouse-cloud-api/src/models.rs +++ b/crates/clickhouse-cloud-api/src/models.rs @@ -199,18 +199,22 @@ pub use organizations::{ }; pub use postgres::{ BasePostgresService, PgBouncerConfig, PgBouncerConfigResponse, PgConfig, - PgConfigDefaultTransactionIsolation, PgConfigResponse, PgConfigSslMinProtocolVersion, - PgConfigWalCompression, PgCreatedAtProperty, PgHaType, PgIdProperty, PgIsPrimaryProperty, - PgNameProperty, PgPassword, PgPitrRestoreTargetProperty, PgProvider, PgRegion, PgSize, - PgStateProperty, PgStorageSize, PgTags, PgTagsResponse, PgVersion, PostgresBackup, - PostgresInstanceConfig, PostgresInstanceConfigResponse, PostgresInstanceUpdateConfigResponse, - PostgresLogEntry, PostgresLogsGetListSortorder, PostgresMetric, PostgresMetricDataPoint, - PostgresMetricSeries, PostgresMetrics, PostgresQueryExecution, PostgresService, - PostgresServiceListItem, PostgresServicePasswordResource, PostgresServicePatchRequest, - PostgresServicePostRequest, PostgresServiceReadReplicaRequest, PostgresServiceRestoreRequest, - PostgresServiceSetPassword, PostgresServiceSetState, PostgresServiceSetStateCommand, - PostgresSlowQueryPattern, PostgresSlowQueryPatternDetail, RunPostgresQueryRequest, - SlowQueryPatternsGetListSortby, SlowQueryPatternsGetListSortorder, + PgConfigDefaultToastCompression, PgConfigDefaultTransactionIsolation, + PgConfigEnableParallelHash, PgConfigIoMethod, PgConfigLogLockWaits, PgConfigResponse, + PgConfigSslMinProtocolVersion, PgConfigWalCompression, PgCreatedAtProperty, PgHaType, + PgIdProperty, PgIsPrimaryProperty, PgNameProperty, PgPassword, PgPitrRestoreTargetProperty, + PgProvider, PgRegion, PgSize, PgStateProperty, PgStorageSize, PgTags, PgTagsResponse, + PgVersion, PostgresBackup, PostgresInstanceConfig, PostgresInstanceConfigResponse, + PostgresInstanceUpdateConfigResponse, PostgresIpAccessListEntry, + PostgresIpAccessListEntryResponse, PostgresIpAccessListPatch, + PostgresIpAccessListRuleReference, PostgresLogEntry, PostgresLogsGetListSortorder, + PostgresMetric, PostgresMetricDataPoint, PostgresMetricSeries, PostgresMetrics, + PostgresQueryExecution, PostgresService, PostgresServiceListItem, + PostgresServicePasswordResource, PostgresServicePatchRequest, PostgresServicePostRequest, + PostgresServiceReadReplicaRequest, PostgresServiceRestoreRequest, PostgresServiceSetPassword, + PostgresServiceSetState, PostgresServiceSetStateCommand, PostgresSlowQueryPattern, + PostgresSlowQueryPatternDetail, RunPostgresQueryRequest, SlowQueryPatternsGetListSortby, + SlowQueryPatternsGetListSortorder, }; pub use query_api_endpoints::{ PublicQueryApiEndpoint, PublicQueryApiEndpointListItem, diff --git a/crates/clickhouse-cloud-api/src/models/postgres.rs b/crates/clickhouse-cloud-api/src/models/postgres.rs index 31fe07395..9999bcc30 100644 --- a/crates/clickhouse-cloud-api/src/models/postgres.rs +++ b/crates/clickhouse-cloud-api/src/models/postgres.rs @@ -362,128 +362,128 @@ pub enum PgSize { R8gd_24xlarge, #[serde(rename = "r8gd.48xlarge")] R8gd_48xlarge, - #[serde(rename = "c4a-highmem-4")] - C4a_highmem_4, - #[serde(rename = "c4a-highmem-8")] - C4a_highmem_8, - #[serde(rename = "c4a-highmem-16")] - C4a_highmem_16, - #[serde(rename = "c4a-highmem-32")] - C4a_highmem_32, - #[serde(rename = "c4a-highmem-48")] - C4a_highmem_48, - #[serde(rename = "c4a-highmem-64")] - C4a_highmem_64, - #[serde(rename = "c4a-highmem-72")] - C4a_highmem_72, - #[serde(rename = "c4a-standard-4")] - C4a_standard_4, - #[serde(rename = "c4a-standard-8")] - C4a_standard_8, - #[serde(rename = "c4a-standard-16")] - C4a_standard_16, - #[serde(rename = "c4a-standard-32")] - C4a_standard_32, - #[serde(rename = "c4a-standard-48")] - C4a_standard_48, - #[serde(rename = "c4a-standard-64")] - C4a_standard_64, - #[serde(rename = "c4a-standard-72")] - C4a_standard_72, - #[serde(rename = "c4-highmem-4")] - C4_highmem_4, - #[serde(rename = "c4-highmem-8")] - C4_highmem_8, - #[serde(rename = "c4-highmem-16")] - C4_highmem_16, - #[serde(rename = "c4-highmem-24")] - C4_highmem_24, - #[serde(rename = "c4-highmem-32")] - C4_highmem_32, - #[serde(rename = "c4-highmem-48")] - C4_highmem_48, - #[serde(rename = "c4-highmem-96")] - C4_highmem_96, - #[serde(rename = "c4-highmem-144")] - C4_highmem_144, - #[serde(rename = "c4-highmem-192")] - C4_highmem_192, - #[serde(rename = "c4-highmem-288")] - C4_highmem_288, - #[serde(rename = "c4-standard-4")] - C4_standard_4, - #[serde(rename = "c4-standard-8")] - C4_standard_8, - #[serde(rename = "c4-standard-16")] - C4_standard_16, - #[serde(rename = "c4-standard-24")] - C4_standard_24, - #[serde(rename = "c4-standard-32")] - C4_standard_32, - #[serde(rename = "c4-standard-48")] - C4_standard_48, - #[serde(rename = "c4-standard-96")] - C4_standard_96, - #[serde(rename = "c4-standard-144")] - C4_standard_144, - #[serde(rename = "c4-standard-192")] - C4_standard_192, - #[serde(rename = "c4-standard-288")] - C4_standard_288, - #[serde(rename = "c4d-highmem-8")] - C4d_highmem_8, - #[serde(rename = "c4d-highmem-16")] - C4d_highmem_16, - #[serde(rename = "c4d-highmem-32")] - C4d_highmem_32, - #[serde(rename = "c4d-highmem-48")] - C4d_highmem_48, - #[serde(rename = "c4d-highmem-64")] - C4d_highmem_64, - #[serde(rename = "c4d-highmem-96")] - C4d_highmem_96, - #[serde(rename = "c4d-highmem-192")] - C4d_highmem_192, - #[serde(rename = "c4d-highmem-384")] - C4d_highmem_384, - #[serde(rename = "c4d-standard-8")] - C4d_standard_8, - #[serde(rename = "c4d-standard-16")] - C4d_standard_16, - #[serde(rename = "c4d-standard-32")] - C4d_standard_32, - #[serde(rename = "c4d-standard-48")] - C4d_standard_48, - #[serde(rename = "c4d-standard-64")] - C4d_standard_64, - #[serde(rename = "c4d-standard-96")] - C4d_standard_96, - #[serde(rename = "c4d-standard-192")] - C4d_standard_192, - #[serde(rename = "c4d-standard-384")] - C4d_standard_384, - #[serde(rename = "z3-highlssd-8")] - Z3_highlssd_8, - #[serde(rename = "z3-highlssd-16")] - Z3_highlssd_16, - #[serde(rename = "z3-highlssd-22")] - Z3_highlssd_22, - #[serde(rename = "z3-highlssd-32")] - Z3_highlssd_32, - #[serde(rename = "z3-highlssd-44")] - Z3_highlssd_44, - #[serde(rename = "z3-highlssd-88")] - Z3_highlssd_88, - #[serde(rename = "z3-standardlssd-14")] - Z3_standardlssd_14, - #[serde(rename = "z3-standardlssd-22")] - Z3_standardlssd_22, - #[serde(rename = "z3-standardlssd-44")] - Z3_standardlssd_44, - #[serde(rename = "z3-standardlssd-88")] - Z3_standardlssd_88, - #[serde(rename = "z3-standardlssd-176")] - Z3_standardlssd_176, + #[serde(rename = "c4a-highmem-lssd-4")] + C4a_highmem_lssd_4, + #[serde(rename = "c4a-highmem-lssd-8")] + C4a_highmem_lssd_8, + #[serde(rename = "c4a-highmem-lssd-16")] + C4a_highmem_lssd_16, + #[serde(rename = "c4a-highmem-lssd-32")] + C4a_highmem_lssd_32, + #[serde(rename = "c4a-highmem-lssd-48")] + C4a_highmem_lssd_48, + #[serde(rename = "c4a-highmem-lssd-64")] + C4a_highmem_lssd_64, + #[serde(rename = "c4a-highmem-lssd-72")] + C4a_highmem_lssd_72, + #[serde(rename = "c4a-standard-lssd-4")] + C4a_standard_lssd_4, + #[serde(rename = "c4a-standard-lssd-8")] + C4a_standard_lssd_8, + #[serde(rename = "c4a-standard-lssd-16")] + C4a_standard_lssd_16, + #[serde(rename = "c4a-standard-lssd-32")] + C4a_standard_lssd_32, + #[serde(rename = "c4a-standard-lssd-48")] + C4a_standard_lssd_48, + #[serde(rename = "c4a-standard-lssd-64")] + C4a_standard_lssd_64, + #[serde(rename = "c4a-standard-lssd-72")] + C4a_standard_lssd_72, + #[serde(rename = "c4-highmem-lssd-4")] + C4_highmem_lssd_4, + #[serde(rename = "c4-highmem-lssd-8")] + C4_highmem_lssd_8, + #[serde(rename = "c4-highmem-lssd-16")] + C4_highmem_lssd_16, + #[serde(rename = "c4-highmem-lssd-24")] + C4_highmem_lssd_24, + #[serde(rename = "c4-highmem-lssd-32")] + C4_highmem_lssd_32, + #[serde(rename = "c4-highmem-lssd-48")] + C4_highmem_lssd_48, + #[serde(rename = "c4-highmem-lssd-96")] + C4_highmem_lssd_96, + #[serde(rename = "c4-highmem-lssd-144")] + C4_highmem_lssd_144, + #[serde(rename = "c4-highmem-lssd-192")] + C4_highmem_lssd_192, + #[serde(rename = "c4-highmem-lssd-288")] + C4_highmem_lssd_288, + #[serde(rename = "c4-standard-lssd-4")] + C4_standard_lssd_4, + #[serde(rename = "c4-standard-lssd-8")] + C4_standard_lssd_8, + #[serde(rename = "c4-standard-lssd-16")] + C4_standard_lssd_16, + #[serde(rename = "c4-standard-lssd-24")] + C4_standard_lssd_24, + #[serde(rename = "c4-standard-lssd-32")] + C4_standard_lssd_32, + #[serde(rename = "c4-standard-lssd-48")] + C4_standard_lssd_48, + #[serde(rename = "c4-standard-lssd-96")] + C4_standard_lssd_96, + #[serde(rename = "c4-standard-lssd-144")] + C4_standard_lssd_144, + #[serde(rename = "c4-standard-lssd-192")] + C4_standard_lssd_192, + #[serde(rename = "c4-standard-lssd-288")] + C4_standard_lssd_288, + #[serde(rename = "c4d-highmem-lssd-8")] + C4d_highmem_lssd_8, + #[serde(rename = "c4d-highmem-lssd-16")] + C4d_highmem_lssd_16, + #[serde(rename = "c4d-highmem-lssd-32")] + C4d_highmem_lssd_32, + #[serde(rename = "c4d-highmem-lssd-48")] + C4d_highmem_lssd_48, + #[serde(rename = "c4d-highmem-lssd-64")] + C4d_highmem_lssd_64, + #[serde(rename = "c4d-highmem-lssd-96")] + C4d_highmem_lssd_96, + #[serde(rename = "c4d-highmem-lssd-192")] + C4d_highmem_lssd_192, + #[serde(rename = "c4d-highmem-lssd-384")] + C4d_highmem_lssd_384, + #[serde(rename = "c4d-standard-lssd-8")] + C4d_standard_lssd_8, + #[serde(rename = "c4d-standard-lssd-16")] + C4d_standard_lssd_16, + #[serde(rename = "c4d-standard-lssd-32")] + C4d_standard_lssd_32, + #[serde(rename = "c4d-standard-lssd-48")] + C4d_standard_lssd_48, + #[serde(rename = "c4d-standard-lssd-64")] + C4d_standard_lssd_64, + #[serde(rename = "c4d-standard-lssd-96")] + C4d_standard_lssd_96, + #[serde(rename = "c4d-standard-lssd-192")] + C4d_standard_lssd_192, + #[serde(rename = "c4d-standard-lssd-384")] + C4d_standard_lssd_384, + #[serde(rename = "z3-highmem-highlssd-8")] + Z3_highmem_highlssd_8, + #[serde(rename = "z3-highmem-highlssd-16")] + Z3_highmem_highlssd_16, + #[serde(rename = "z3-highmem-highlssd-22")] + Z3_highmem_highlssd_22, + #[serde(rename = "z3-highmem-highlssd-32")] + Z3_highmem_highlssd_32, + #[serde(rename = "z3-highmem-highlssd-44")] + Z3_highmem_highlssd_44, + #[serde(rename = "z3-highmem-highlssd-88")] + Z3_highmem_highlssd_88, + #[serde(rename = "z3-highmem-standardlssd-14")] + Z3_highmem_standardlssd_14, + #[serde(rename = "z3-highmem-standardlssd-22")] + Z3_highmem_standardlssd_22, + #[serde(rename = "z3-highmem-standardlssd-44")] + Z3_highmem_standardlssd_44, + #[serde(rename = "z3-highmem-standardlssd-88")] + Z3_highmem_standardlssd_88, + #[serde(rename = "z3-highmem-standardlssd-176")] + Z3_highmem_standardlssd_176, /// Catch-all for unknown or newly-added values. #[serde(untagged)] Unknown(String), @@ -574,67 +574,67 @@ impl std::fmt::Display for PgSize { Self::R8gd_16xlarge => write!(f, "r8gd.16xlarge"), Self::R8gd_24xlarge => write!(f, "r8gd.24xlarge"), Self::R8gd_48xlarge => write!(f, "r8gd.48xlarge"), - Self::C4a_highmem_4 => write!(f, "c4a-highmem-4"), - Self::C4a_highmem_8 => write!(f, "c4a-highmem-8"), - Self::C4a_highmem_16 => write!(f, "c4a-highmem-16"), - Self::C4a_highmem_32 => write!(f, "c4a-highmem-32"), - Self::C4a_highmem_48 => write!(f, "c4a-highmem-48"), - Self::C4a_highmem_64 => write!(f, "c4a-highmem-64"), - Self::C4a_highmem_72 => write!(f, "c4a-highmem-72"), - Self::C4a_standard_4 => write!(f, "c4a-standard-4"), - Self::C4a_standard_8 => write!(f, "c4a-standard-8"), - Self::C4a_standard_16 => write!(f, "c4a-standard-16"), - Self::C4a_standard_32 => write!(f, "c4a-standard-32"), - Self::C4a_standard_48 => write!(f, "c4a-standard-48"), - Self::C4a_standard_64 => write!(f, "c4a-standard-64"), - Self::C4a_standard_72 => write!(f, "c4a-standard-72"), - Self::C4_highmem_4 => write!(f, "c4-highmem-4"), - Self::C4_highmem_8 => write!(f, "c4-highmem-8"), - Self::C4_highmem_16 => write!(f, "c4-highmem-16"), - Self::C4_highmem_24 => write!(f, "c4-highmem-24"), - Self::C4_highmem_32 => write!(f, "c4-highmem-32"), - Self::C4_highmem_48 => write!(f, "c4-highmem-48"), - Self::C4_highmem_96 => write!(f, "c4-highmem-96"), - Self::C4_highmem_144 => write!(f, "c4-highmem-144"), - Self::C4_highmem_192 => write!(f, "c4-highmem-192"), - Self::C4_highmem_288 => write!(f, "c4-highmem-288"), - Self::C4_standard_4 => write!(f, "c4-standard-4"), - Self::C4_standard_8 => write!(f, "c4-standard-8"), - Self::C4_standard_16 => write!(f, "c4-standard-16"), - Self::C4_standard_24 => write!(f, "c4-standard-24"), - Self::C4_standard_32 => write!(f, "c4-standard-32"), - Self::C4_standard_48 => write!(f, "c4-standard-48"), - Self::C4_standard_96 => write!(f, "c4-standard-96"), - Self::C4_standard_144 => write!(f, "c4-standard-144"), - Self::C4_standard_192 => write!(f, "c4-standard-192"), - Self::C4_standard_288 => write!(f, "c4-standard-288"), - Self::C4d_highmem_8 => write!(f, "c4d-highmem-8"), - Self::C4d_highmem_16 => write!(f, "c4d-highmem-16"), - Self::C4d_highmem_32 => write!(f, "c4d-highmem-32"), - Self::C4d_highmem_48 => write!(f, "c4d-highmem-48"), - Self::C4d_highmem_64 => write!(f, "c4d-highmem-64"), - Self::C4d_highmem_96 => write!(f, "c4d-highmem-96"), - Self::C4d_highmem_192 => write!(f, "c4d-highmem-192"), - Self::C4d_highmem_384 => write!(f, "c4d-highmem-384"), - Self::C4d_standard_8 => write!(f, "c4d-standard-8"), - Self::C4d_standard_16 => write!(f, "c4d-standard-16"), - Self::C4d_standard_32 => write!(f, "c4d-standard-32"), - Self::C4d_standard_48 => write!(f, "c4d-standard-48"), - Self::C4d_standard_64 => write!(f, "c4d-standard-64"), - Self::C4d_standard_96 => write!(f, "c4d-standard-96"), - Self::C4d_standard_192 => write!(f, "c4d-standard-192"), - Self::C4d_standard_384 => write!(f, "c4d-standard-384"), - Self::Z3_highlssd_8 => write!(f, "z3-highlssd-8"), - Self::Z3_highlssd_16 => write!(f, "z3-highlssd-16"), - Self::Z3_highlssd_22 => write!(f, "z3-highlssd-22"), - Self::Z3_highlssd_32 => write!(f, "z3-highlssd-32"), - Self::Z3_highlssd_44 => write!(f, "z3-highlssd-44"), - Self::Z3_highlssd_88 => write!(f, "z3-highlssd-88"), - Self::Z3_standardlssd_14 => write!(f, "z3-standardlssd-14"), - Self::Z3_standardlssd_22 => write!(f, "z3-standardlssd-22"), - Self::Z3_standardlssd_44 => write!(f, "z3-standardlssd-44"), - Self::Z3_standardlssd_88 => write!(f, "z3-standardlssd-88"), - Self::Z3_standardlssd_176 => write!(f, "z3-standardlssd-176"), + Self::C4a_highmem_lssd_4 => write!(f, "c4a-highmem-lssd-4"), + Self::C4a_highmem_lssd_8 => write!(f, "c4a-highmem-lssd-8"), + Self::C4a_highmem_lssd_16 => write!(f, "c4a-highmem-lssd-16"), + Self::C4a_highmem_lssd_32 => write!(f, "c4a-highmem-lssd-32"), + Self::C4a_highmem_lssd_48 => write!(f, "c4a-highmem-lssd-48"), + Self::C4a_highmem_lssd_64 => write!(f, "c4a-highmem-lssd-64"), + Self::C4a_highmem_lssd_72 => write!(f, "c4a-highmem-lssd-72"), + Self::C4a_standard_lssd_4 => write!(f, "c4a-standard-lssd-4"), + Self::C4a_standard_lssd_8 => write!(f, "c4a-standard-lssd-8"), + Self::C4a_standard_lssd_16 => write!(f, "c4a-standard-lssd-16"), + Self::C4a_standard_lssd_32 => write!(f, "c4a-standard-lssd-32"), + Self::C4a_standard_lssd_48 => write!(f, "c4a-standard-lssd-48"), + Self::C4a_standard_lssd_64 => write!(f, "c4a-standard-lssd-64"), + Self::C4a_standard_lssd_72 => write!(f, "c4a-standard-lssd-72"), + Self::C4_highmem_lssd_4 => write!(f, "c4-highmem-lssd-4"), + Self::C4_highmem_lssd_8 => write!(f, "c4-highmem-lssd-8"), + Self::C4_highmem_lssd_16 => write!(f, "c4-highmem-lssd-16"), + Self::C4_highmem_lssd_24 => write!(f, "c4-highmem-lssd-24"), + Self::C4_highmem_lssd_32 => write!(f, "c4-highmem-lssd-32"), + Self::C4_highmem_lssd_48 => write!(f, "c4-highmem-lssd-48"), + Self::C4_highmem_lssd_96 => write!(f, "c4-highmem-lssd-96"), + Self::C4_highmem_lssd_144 => write!(f, "c4-highmem-lssd-144"), + Self::C4_highmem_lssd_192 => write!(f, "c4-highmem-lssd-192"), + Self::C4_highmem_lssd_288 => write!(f, "c4-highmem-lssd-288"), + Self::C4_standard_lssd_4 => write!(f, "c4-standard-lssd-4"), + Self::C4_standard_lssd_8 => write!(f, "c4-standard-lssd-8"), + Self::C4_standard_lssd_16 => write!(f, "c4-standard-lssd-16"), + Self::C4_standard_lssd_24 => write!(f, "c4-standard-lssd-24"), + Self::C4_standard_lssd_32 => write!(f, "c4-standard-lssd-32"), + Self::C4_standard_lssd_48 => write!(f, "c4-standard-lssd-48"), + Self::C4_standard_lssd_96 => write!(f, "c4-standard-lssd-96"), + Self::C4_standard_lssd_144 => write!(f, "c4-standard-lssd-144"), + Self::C4_standard_lssd_192 => write!(f, "c4-standard-lssd-192"), + Self::C4_standard_lssd_288 => write!(f, "c4-standard-lssd-288"), + Self::C4d_highmem_lssd_8 => write!(f, "c4d-highmem-lssd-8"), + Self::C4d_highmem_lssd_16 => write!(f, "c4d-highmem-lssd-16"), + Self::C4d_highmem_lssd_32 => write!(f, "c4d-highmem-lssd-32"), + Self::C4d_highmem_lssd_48 => write!(f, "c4d-highmem-lssd-48"), + Self::C4d_highmem_lssd_64 => write!(f, "c4d-highmem-lssd-64"), + Self::C4d_highmem_lssd_96 => write!(f, "c4d-highmem-lssd-96"), + Self::C4d_highmem_lssd_192 => write!(f, "c4d-highmem-lssd-192"), + Self::C4d_highmem_lssd_384 => write!(f, "c4d-highmem-lssd-384"), + Self::C4d_standard_lssd_8 => write!(f, "c4d-standard-lssd-8"), + Self::C4d_standard_lssd_16 => write!(f, "c4d-standard-lssd-16"), + Self::C4d_standard_lssd_32 => write!(f, "c4d-standard-lssd-32"), + Self::C4d_standard_lssd_48 => write!(f, "c4d-standard-lssd-48"), + Self::C4d_standard_lssd_64 => write!(f, "c4d-standard-lssd-64"), + Self::C4d_standard_lssd_96 => write!(f, "c4d-standard-lssd-96"), + Self::C4d_standard_lssd_192 => write!(f, "c4d-standard-lssd-192"), + Self::C4d_standard_lssd_384 => write!(f, "c4d-standard-lssd-384"), + Self::Z3_highmem_highlssd_8 => write!(f, "z3-highmem-highlssd-8"), + Self::Z3_highmem_highlssd_16 => write!(f, "z3-highmem-highlssd-16"), + Self::Z3_highmem_highlssd_22 => write!(f, "z3-highmem-highlssd-22"), + Self::Z3_highmem_highlssd_32 => write!(f, "z3-highmem-highlssd-32"), + Self::Z3_highmem_highlssd_44 => write!(f, "z3-highmem-highlssd-44"), + Self::Z3_highmem_highlssd_88 => write!(f, "z3-highmem-highlssd-88"), + Self::Z3_highmem_standardlssd_14 => write!(f, "z3-highmem-standardlssd-14"), + Self::Z3_highmem_standardlssd_22 => write!(f, "z3-highmem-standardlssd-22"), + Self::Z3_highmem_standardlssd_44 => write!(f, "z3-highmem-standardlssd-44"), + Self::Z3_highmem_standardlssd_88 => write!(f, "z3-highmem-standardlssd-88"), + Self::Z3_highmem_standardlssd_176 => write!(f, "z3-highmem-standardlssd-176"), Self::Unknown(s) => write!(f, "{s}"), } } @@ -804,6 +804,121 @@ impl PgConfigSslMinProtocolVersion { pub const VALUES: &'static [&'static str] = &["TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"]; } +/// Inline enum for `pgConfig.default_toast_compression`. +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] +pub enum PgConfigDefaultToastCompression { + #[serde(rename = "pglz")] + #[default] + Pglz, + #[serde(rename = "lz4")] + Lz4, + /// Catch-all for unknown or newly-added values. + #[serde(untagged)] + Unknown(String), +} + +impl std::fmt::Display for PgConfigDefaultToastCompression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Pglz => write!(f, "pglz"), + Self::Lz4 => write!(f, "lz4"), + Self::Unknown(s) => write!(f, "{s}"), + } + } +} + +impl PgConfigDefaultToastCompression { + /// Wire values accepted by the API, excluding the catch-all. + pub const VALUES: &'static [&'static str] = &["pglz", "lz4"]; +} + +/// Inline enum for `pgConfig.enable_parallel_hash`. +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] +pub enum PgConfigEnableParallelHash { + #[serde(rename = "on")] + #[default] + On, + #[serde(rename = "off")] + Off, + /// Catch-all for unknown or newly-added values. + #[serde(untagged)] + Unknown(String), +} + +impl std::fmt::Display for PgConfigEnableParallelHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::On => write!(f, "on"), + Self::Off => write!(f, "off"), + Self::Unknown(s) => write!(f, "{s}"), + } + } +} + +impl PgConfigEnableParallelHash { + /// Wire values accepted by the API, excluding the catch-all. + pub const VALUES: &'static [&'static str] = &["on", "off"]; +} + +/// Inline enum for `pgConfig.io_method`. +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] +pub enum PgConfigIoMethod { + #[serde(rename = "sync")] + #[default] + Sync, + #[serde(rename = "worker")] + Worker, + #[serde(rename = "io_uring")] + Io_uring, + /// Catch-all for unknown or newly-added values. + #[serde(untagged)] + Unknown(String), +} + +impl std::fmt::Display for PgConfigIoMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Sync => write!(f, "sync"), + Self::Worker => write!(f, "worker"), + Self::Io_uring => write!(f, "io_uring"), + Self::Unknown(s) => write!(f, "{s}"), + } + } +} + +impl PgConfigIoMethod { + /// Wire values accepted by the API, excluding the catch-all. + pub const VALUES: &'static [&'static str] = &["sync", "worker", "io_uring"]; +} + +/// Inline enum for `pgConfig.log_lock_waits`. +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] +pub enum PgConfigLogLockWaits { + #[serde(rename = "on")] + #[default] + On, + #[serde(rename = "off")] + Off, + /// Catch-all for unknown or newly-added values. + #[serde(untagged)] + Unknown(String), +} + +impl std::fmt::Display for PgConfigLogLockWaits { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::On => write!(f, "on"), + Self::Off => write!(f, "off"), + Self::Unknown(s) => write!(f, "{s}"), + } + } +} + +impl PgConfigLogLockWaits { + /// Wire values accepted by the API, excluding the catch-all. + pub const VALUES: &'static [&'static str] = &["on", "off"]; +} + /// Inline enum for `pgConfig.wal_compression`. #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] pub enum PgConfigWalCompression { @@ -883,6 +998,56 @@ pub struct BasePostgresService { pub tags: PgTags, } +/// `PostgresIpAccessListEntry` from the ClickHouse Cloud API: one IPv4 TCP +/// access rule. Omit `id` to create a rule; include the returned `id` to edit it. +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] +pub struct PostgresIpAccessListEntry { + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + /// Port (`"5432"`, `"6432"`) or inclusive range (`"5432..6432"`). + #[serde(rename = "portRange")] + pub port_range: String, + /// IPv4 address or CIDR. + pub source: String, +} + +/// `PostgresIpAccessListEntry` from the ClickHouse Cloud API, in response +/// position. +/// +/// Response variant of [`PostgresIpAccessListEntry`]: every field is +/// `Option`, so a field the API drops or sends as `null` deserializes to +/// `None` instead of failing. Writing a fetched rule back goes through +/// `TryFrom` (see [`crate::convert`]). +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] +pub struct PostgresIpAccessListEntryResponse { + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(rename = "portRange", skip_serializing_if = "Option::is_none")] + pub port_range: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub source: Option, +} + +/// `PostgresIpAccessListPatch` from the ClickHouse Cloud API. +/// +/// The API applies `remove` before `add`. Updates can partially succeed, so +/// read the current list before retrying. +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] +pub struct PostgresIpAccessListPatch { + pub add: Vec, + pub remove: Vec, +} + +/// `PostgresIpAccessListRuleReference` from the ClickHouse Cloud API. +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] +pub struct PostgresIpAccessListRuleReference { + pub id: String, +} + /// `PostgresService` from the ClickHouse Cloud API. #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] pub struct PostgresService { @@ -896,6 +1061,8 @@ pub struct PostgresService { pub hostname: Option, #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, + #[serde(rename = "ipAccessList", skip_serializing_if = "Option::is_none")] + pub ip_access_list: Option>, #[serde(rename = "isPrimary", skip_serializing_if = "Option::is_none")] pub is_primary: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -959,6 +1126,8 @@ pub struct PostgresServicePasswordResource { pub struct PostgresServicePatchRequest { #[serde(rename = "haType", skip_serializing_if = "Option::is_none")] pub ha_type: Option, + #[serde(rename = "ipAccessList", skip_serializing_if = "Option::is_none")] + pub ip_access_list: Option, #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -972,6 +1141,10 @@ pub struct PostgresServicePatchRequest { pub struct PostgresServicePostRequest { #[serde(rename = "haType", skip_serializing_if = "Option::is_none")] pub ha_type: Option, + /// IPv4 TCP access rules applied at creation. `None` allows all addresses + /// on 5432 and 6432; `Some(vec![])` blocks public access. + #[serde(rename = "ipAccessList", skip_serializing_if = "Option::is_none")] + pub ip_access_list: Option>, pub name: PgNameProperty, #[serde(rename = "pgBouncerConfig", skip_serializing_if = "Option::is_none")] pub pg_bouncer_config: Option, @@ -1278,32 +1451,70 @@ pub struct PgConfig { #[serde(skip_serializing_if = "Option::is_none")] pub autovacuum_work_mem: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub bgwriter_delay: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub bgwriter_flush_after: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub bgwriter_lru_maxpages: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub bgwriter_lru_multiplier: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub checkpoint_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub default_toast_compression: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub default_transaction_isolation: Option, #[serde(skip_serializing_if = "Option::is_none")] pub effective_cache_size: Option, #[serde(skip_serializing_if = "Option::is_none")] pub effective_io_concurrency: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub enable_parallel_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub hash_mem_multiplier: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub idle_in_transaction_session_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] pub idle_session_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub io_method: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub io_workers: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub lock_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub log_lock_waits: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub log_min_duration_statement: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub log_parameter_max_length: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub log_temp_files: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub logical_decoding_work_mem: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub maintenance_io_concurrency: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub maintenance_work_mem: Option, #[serde(skip_serializing_if = "Option::is_none")] pub max_connections: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub max_logical_replication_workers: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub max_parallel_maintenance_workers: Option, #[serde(skip_serializing_if = "Option::is_none")] pub max_parallel_workers: Option, #[serde(skip_serializing_if = "Option::is_none")] pub max_parallel_workers_per_gather: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub max_replication_slots: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub max_slot_wal_keep_size: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub max_sync_workers_per_subscription: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_wal_senders: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub max_wal_size: Option, #[serde(skip_serializing_if = "Option::is_none")] pub max_worker_processes: Option, @@ -1318,8 +1529,14 @@ pub struct PgConfig { #[serde(skip_serializing_if = "Option::is_none")] pub statement_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub temp_buffers: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub track_activity_query_size: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub transaction_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub vacuum_cost_delay: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub wal_buffers: Option, #[serde(skip_serializing_if = "Option::is_none")] pub wal_compression: Option, @@ -1362,32 +1579,70 @@ pub struct PgConfigResponse { #[serde(skip_serializing_if = "Option::is_none")] pub autovacuum_work_mem: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub bgwriter_delay: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub bgwriter_flush_after: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub bgwriter_lru_maxpages: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub bgwriter_lru_multiplier: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub checkpoint_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub default_toast_compression: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub default_transaction_isolation: Option, #[serde(skip_serializing_if = "Option::is_none")] pub effective_cache_size: Option, #[serde(skip_serializing_if = "Option::is_none")] pub effective_io_concurrency: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub enable_parallel_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub hash_mem_multiplier: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub idle_in_transaction_session_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] pub idle_session_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub io_method: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub io_workers: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub lock_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub log_lock_waits: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub log_min_duration_statement: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub log_parameter_max_length: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub log_temp_files: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub logical_decoding_work_mem: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub maintenance_io_concurrency: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub maintenance_work_mem: Option, #[serde(skip_serializing_if = "Option::is_none")] pub max_connections: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub max_logical_replication_workers: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub max_parallel_maintenance_workers: Option, #[serde(skip_serializing_if = "Option::is_none")] pub max_parallel_workers: Option, #[serde(skip_serializing_if = "Option::is_none")] pub max_parallel_workers_per_gather: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub max_replication_slots: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub max_slot_wal_keep_size: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub max_sync_workers_per_subscription: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_wal_senders: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub max_wal_size: Option, #[serde(skip_serializing_if = "Option::is_none")] pub max_worker_processes: Option, @@ -1402,8 +1657,14 @@ pub struct PgConfigResponse { #[serde(skip_serializing_if = "Option::is_none")] pub statement_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub temp_buffers: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub track_activity_query_size: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub transaction_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub vacuum_cost_delay: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub wal_buffers: Option, #[serde(skip_serializing_if = "Option::is_none")] pub wal_compression: Option, diff --git a/crates/clickhouse-cloud-api/tests/client_test.rs b/crates/clickhouse-cloud-api/tests/client_test.rs index 996d42781..1203ca5fc 100644 --- a/crates/clickhouse-cloud-api/tests/client_test.rs +++ b/crates/clickhouse-cloud-api/tests/client_test.rs @@ -4610,6 +4610,49 @@ async fn update_postgres_service() { assert_eq!(pg.name.as_deref(), Some("pg-1")); } +#[tokio::test] +async fn update_postgres_service_ip_access_list() { + let (s, c) = setup().await; + + Mock::given(method("PATCH")) + .and(path("/v1/organizations/org-1/postgres/pg-1")) + .and(body_json(serde_json::json!({"ipAccessList": { + "add": [{"source": "203.0.113.0/24", "portRange": "5432"}], + "remove": [{"id": "rule-old"}] + }}))) + .respond_with(ok_json(serde_json::json!({ + "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "ipAccessList": [ + {"id": "rule-new", "source": "203.0.113.0/24", "portRange": "5432"} + ] + }))) + .mount(&s) + .await; + + let body = PostgresServicePatchRequest { + ip_access_list: Some(PostgresIpAccessListPatch { + add: vec![PostgresIpAccessListEntry { + source: "203.0.113.0/24".into(), + port_range: "5432".into(), + ..Default::default() + }], + remove: vec![PostgresIpAccessListRuleReference { + id: "rule-old".into(), + }], + }), + ..Default::default() + }; + let pg = c + .postgres_service_patch("org-1", "pg-1", &body) + .await + .unwrap() + .result + .unwrap(); + let rules = pg.ip_access_list.unwrap(); + assert_eq!(rules.len(), 1); + assert_eq!(rules[0].id.as_deref(), Some("rule-new")); +} + #[tokio::test] async fn delete_postgres_service() { let (s, c) = setup().await; diff --git a/crates/clickhouse-cloud-api/tests/integration_postgres_test.rs b/crates/clickhouse-cloud-api/tests/integration_postgres_test.rs index 4d15cf57b..c1accc7b3 100644 --- a/crates/clickhouse-cloud-api/tests/integration_postgres_test.rs +++ b/crates/clickhouse-cloud-api/tests/integration_postgres_test.rs @@ -497,6 +497,68 @@ async fn cloud_postgres_crud_lifecycle() -> TestResult<()> { ) .await?; + // ── IP access list ────────────────────────────────────────── + + // Adds a TEST-NET-3 rule next to the default allow-all rules, reads + // it back, then removes it by its returned ID. The allow-all rules + // stay in place so later connection steps keep working. + log_phase("IP access list"); + let test_source = "203.0.113.0/24"; + failures + .run( + &ctx, + StepKind::NonBlocking, + "add, read back and remove a postgres IP access rule", + || { + let client = client.clone(); + let org_id = ctx.org_id.clone(); + let postgres_id = postgres_id.clone(); + async move { + let add = PostgresServicePatchRequest { + ip_access_list: Some(PostgresIpAccessListPatch { + add: vec![PostgresIpAccessListEntry { + source: test_source.to_string(), + port_range: "5432".to_string(), + description: Some("clickhousectl integration".to_string()), + id: None, + }], + remove: Vec::new(), + }), + ..Default::default() + }; + client + .postgres_service_patch(&org_id, &postgres_id, &add) + .await?; + let svc = client + .postgres_service_get(&org_id, &postgres_id) + .await? + .result + .ok_or("postgres get returned no result")?; + let rule_id = svc + .ip_access_list + .iter() + .flatten() + .find(|rule| rule.source.as_deref() == Some(test_source)) + .and_then(|rule| rule.id.clone()) + .ok_or("added IP access rule not returned with an id by get")?; + let remove = PostgresServicePatchRequest { + ip_access_list: Some(PostgresIpAccessListPatch { + add: Vec::new(), + remove: vec![PostgresIpAccessListRuleReference { + id: rule_id, + }], + }), + ..Default::default() + }; + client + .postgres_service_patch(&org_id, &postgres_id, &remove) + .await?; + Ok(()) + } + }, + ) + .await?; + // ── Password ──────────────────────────────────────────────── // Per OpenAPI spec, PostgresServicePasswordResource.password is only diff --git a/crates/clickhouse-cloud-api/tests/model_facade_test.rs b/crates/clickhouse-cloud-api/tests/model_facade_test.rs index ad766eadf..b76aa1757 100644 --- a/crates/clickhouse-cloud-api/tests/model_facade_test.rs +++ b/crates/clickhouse-cloud-api/tests/model_facade_test.rs @@ -267,3 +267,23 @@ fn notification_is_available_through_both_public_paths() { api::models::Notification::default(), ); } + +#[test] +fn postgres_ip_access_list_models_are_available_through_both_public_paths() { + assert_same_type( + api::PostgresIpAccessListEntry::default(), + api::models::PostgresIpAccessListEntry::default(), + ); + assert_same_type( + api::PostgresIpAccessListEntryResponse::default(), + api::models::PostgresIpAccessListEntryResponse::default(), + ); + assert_same_type( + api::PostgresIpAccessListPatch::default(), + api::models::PostgresIpAccessListPatch::default(), + ); + assert_same_type( + api::PostgresIpAccessListRuleReference::default(), + api::models::PostgresIpAccessListRuleReference::default(), + ); +} diff --git a/crates/clickhouse-cloud-api/tests/models_test.rs b/crates/clickhouse-cloud-api/tests/models_test.rs index 49eaf5e0c..cdf81ba99 100644 --- a/crates/clickhouse-cloud-api/tests/models_test.rs +++ b/crates/clickhouse-cloud-api/tests/models_test.rs @@ -1056,67 +1056,67 @@ fn postgres_slow_query_sort_values_roundtrip_as_known_variants() { #[test] fn postgres_gcp_sizes_roundtrip_as_known_variants() { let sizes = [ - "c4a-highmem-4", - "c4a-highmem-8", - "c4a-highmem-16", - "c4a-highmem-32", - "c4a-highmem-48", - "c4a-highmem-64", - "c4a-highmem-72", - "c4a-standard-4", - "c4a-standard-8", - "c4a-standard-16", - "c4a-standard-32", - "c4a-standard-48", - "c4a-standard-64", - "c4a-standard-72", - "c4-highmem-4", - "c4-highmem-8", - "c4-highmem-16", - "c4-highmem-24", - "c4-highmem-32", - "c4-highmem-48", - "c4-highmem-96", - "c4-highmem-144", - "c4-highmem-192", - "c4-highmem-288", - "c4-standard-4", - "c4-standard-8", - "c4-standard-16", - "c4-standard-24", - "c4-standard-32", - "c4-standard-48", - "c4-standard-96", - "c4-standard-144", - "c4-standard-192", - "c4-standard-288", - "c4d-highmem-8", - "c4d-highmem-16", - "c4d-highmem-32", - "c4d-highmem-48", - "c4d-highmem-64", - "c4d-highmem-96", - "c4d-highmem-192", - "c4d-highmem-384", - "c4d-standard-8", - "c4d-standard-16", - "c4d-standard-32", - "c4d-standard-48", - "c4d-standard-64", - "c4d-standard-96", - "c4d-standard-192", - "c4d-standard-384", - "z3-highlssd-8", - "z3-highlssd-16", - "z3-highlssd-22", - "z3-highlssd-32", - "z3-highlssd-44", - "z3-highlssd-88", - "z3-standardlssd-14", - "z3-standardlssd-22", - "z3-standardlssd-44", - "z3-standardlssd-88", - "z3-standardlssd-176", + "c4a-highmem-lssd-4", + "c4a-highmem-lssd-8", + "c4a-highmem-lssd-16", + "c4a-highmem-lssd-32", + "c4a-highmem-lssd-48", + "c4a-highmem-lssd-64", + "c4a-highmem-lssd-72", + "c4a-standard-lssd-4", + "c4a-standard-lssd-8", + "c4a-standard-lssd-16", + "c4a-standard-lssd-32", + "c4a-standard-lssd-48", + "c4a-standard-lssd-64", + "c4a-standard-lssd-72", + "c4-highmem-lssd-4", + "c4-highmem-lssd-8", + "c4-highmem-lssd-16", + "c4-highmem-lssd-24", + "c4-highmem-lssd-32", + "c4-highmem-lssd-48", + "c4-highmem-lssd-96", + "c4-highmem-lssd-144", + "c4-highmem-lssd-192", + "c4-highmem-lssd-288", + "c4-standard-lssd-4", + "c4-standard-lssd-8", + "c4-standard-lssd-16", + "c4-standard-lssd-24", + "c4-standard-lssd-32", + "c4-standard-lssd-48", + "c4-standard-lssd-96", + "c4-standard-lssd-144", + "c4-standard-lssd-192", + "c4-standard-lssd-288", + "c4d-highmem-lssd-8", + "c4d-highmem-lssd-16", + "c4d-highmem-lssd-32", + "c4d-highmem-lssd-48", + "c4d-highmem-lssd-64", + "c4d-highmem-lssd-96", + "c4d-highmem-lssd-192", + "c4d-highmem-lssd-384", + "c4d-standard-lssd-8", + "c4d-standard-lssd-16", + "c4d-standard-lssd-32", + "c4d-standard-lssd-48", + "c4d-standard-lssd-64", + "c4d-standard-lssd-96", + "c4d-standard-lssd-192", + "c4d-standard-lssd-384", + "z3-highmem-highlssd-8", + "z3-highmem-highlssd-16", + "z3-highmem-highlssd-22", + "z3-highmem-highlssd-32", + "z3-highmem-highlssd-44", + "z3-highmem-highlssd-88", + "z3-highmem-standardlssd-14", + "z3-highmem-standardlssd-22", + "z3-highmem-standardlssd-44", + "z3-highmem-standardlssd-88", + "z3-highmem-standardlssd-176", ]; for wire_value in sizes { @@ -2479,6 +2479,134 @@ fn postgres_instance_config_response_conversion_reports_missing_required_fields( ); } +#[test] +fn postgres_ip_access_list_response_tolerates_absence_and_null() { + for wire in [ + serde_json::json!({}), + serde_json::json!({"ipAccessList": null}), + ] { + let service: PostgresService = serde_json::from_value(wire).unwrap(); + assert_eq!(service.ip_access_list, None); + } + for wire in [ + serde_json::json!({}), + serde_json::json!({"id": null, "source": null, "portRange": null, "description": null}), + ] { + let entry: PostgresIpAccessListEntryResponse = serde_json::from_value(wire).unwrap(); + assert_eq!(entry, PostgresIpAccessListEntryResponse::default()); + assert_eq!(serde_json::to_value(entry).unwrap(), serde_json::json!({})); + } + + let service: PostgresService = serde_json::from_value(serde_json::json!({ + "ipAccessList": [ + {"id": "rule-1", "source": "203.0.113.0/24", "portRange": "5432", "description": "office"}, + {"source": "198.51.100.7/32", "futureField": true} + ] + })) + .unwrap(); + let rules = service.ip_access_list.unwrap(); + assert_eq!(rules[0].id.as_deref(), Some("rule-1")); + assert_eq!(rules[0].port_range.as_deref(), Some("5432")); + assert_eq!(rules[1].source.as_deref(), Some("198.51.100.7/32")); + assert_eq!(rules[1].port_range, None); +} + +#[test] +fn postgres_ip_access_list_requests_are_strict() { + let entry: PostgresIpAccessListEntry = serde_json::from_value( + serde_json::json!({"source": "203.0.113.0/24", "portRange": "5432"}), + ) + .unwrap(); + assert_eq!( + serde_json::to_value(&entry).unwrap(), + serde_json::json!({"source": "203.0.113.0/24", "portRange": "5432"}) + ); + for required in ["source", "portRange"] { + let mut wire = serde_json::json!({"source": "203.0.113.0/24", "portRange": "5432"}); + wire.as_object_mut().unwrap().remove(required); + assert!( + serde_json::from_value::(wire).is_err(), + "{required} must be required" + ); + } + assert!( + serde_json::from_value::(serde_json::json!({})).is_err() + ); + + let patch = PostgresServicePatchRequest { + ip_access_list: Some(PostgresIpAccessListPatch { + add: vec![PostgresIpAccessListEntry { + id: Some("rule-1".into()), + source: "203.0.113.0/24".into(), + port_range: "5432..6432".into(), + description: Some("office".into()), + }], + remove: vec![PostgresIpAccessListRuleReference { + id: "rule-2".into(), + }], + }), + ..Default::default() + }; + assert_eq!( + serde_json::to_value(&patch).unwrap(), + serde_json::json!({"ipAccessList": { + "add": [{"id": "rule-1", "source": "203.0.113.0/24", "portRange": "5432..6432", "description": "office"}], + "remove": [{"id": "rule-2"}] + }}) + ); + + let blocked = PostgresServicePostRequest { + name: "pg".into(), + provider: PgProvider::Aws, + region: "us-east-1".into(), + size: PgSize::C6gd_large, + ip_access_list: Some(Vec::new()), + ..Default::default() + }; + assert_eq!( + serde_json::to_value(&blocked).unwrap()["ipAccessList"], + serde_json::json!([]) + ); + let open = PostgresServicePostRequest { + ip_access_list: None, + ..blocked + }; + assert!( + serde_json::to_value(&open) + .unwrap() + .get("ipAccessList") + .is_none() + ); +} + +#[test] +fn postgres_ip_access_list_entry_response_converts_back_into_an_edit() { + let edit = PostgresIpAccessListEntry::try_from(PostgresIpAccessListEntryResponse { + id: Some("rule-1".into()), + source: Some("203.0.113.0/24".into()), + port_range: Some("5432".into()), + description: None, + }) + .unwrap(); + assert_eq!( + serde_json::to_value(&edit).unwrap(), + serde_json::json!({"id": "rule-1", "source": "203.0.113.0/24", "portRange": "5432"}) + ); + + let missing = PostgresIpAccessListEntry::try_from(PostgresIpAccessListEntryResponse::default()) + .unwrap_err(); + assert_eq!(missing.fields(), ["portRange", "source"]); +} + +#[test] +fn retired_postgres_gcp_size_names_fall_through_to_the_catch_all() { + for wire_value in ["c4-highmem-16", "c4a-standard-4", "z3-highlssd-8"] { + let size: PgSize = serde_json::from_value(wire_value.into()).unwrap(); + assert_eq!(size, PgSize::Unknown(wire_value.to_string())); + assert_eq!(size.to_string(), wire_value); + } +} + #[test] fn backup_minimal_response() { let b: Backup = serde_json::from_str("{}").unwrap(); @@ -9039,3 +9167,50 @@ fn notification_tolerates_missing_null_and_future_values() { assert_eq!(future.severity.as_deref(), Some("future.severity")); assert_eq!(future.resource_type.as_deref(), Some("future.resource")); } + +#[test] +fn pg_config_background_writer_io_and_replication_settings_round_trip() { + let wire = serde_json::json!({ + "bgwriter_delay": "200ms", "bgwriter_flush_after": "512kB", "bgwriter_lru_maxpages": 100, + "bgwriter_lru_multiplier": 2, "default_toast_compression": "lz4", + "enable_parallel_hash": "on", "hash_mem_multiplier": 2, "io_method": "io_uring", + "io_workers": 3, "log_lock_waits": "off", "log_min_duration_statement": -1, + "log_parameter_max_length": "-1", "log_temp_files": 0, + "logical_decoding_work_mem": "64MB", "maintenance_io_concurrency": 10, + "max_logical_replication_workers": 4, "max_replication_slots": 10, + "max_sync_workers_per_subscription": 2, "max_wal_senders": 10, "temp_buffers": "8MB", + "track_activity_query_size": 4096, "vacuum_cost_delay": "0" + }); + let response: PgConfigResponse = serde_json::from_value(wire.clone()).unwrap(); + assert_eq!(response.io_method, Some(PgConfigIoMethod::Io_uring)); + let request = PgConfig::from(response); + assert_eq!( + request.default_toast_compression, + Some(PgConfigDefaultToastCompression::Lz4) + ); + assert_eq!(serde_json::to_value(&request).unwrap(), wire); + + for (values, parse) in [ + ( + PgConfigDefaultToastCompression::VALUES, + (|v| { + serde_json::from_value::(v).map(|e| e.to_string()) + }) as fn(serde_json::Value) -> serde_json::Result, + ), + (PgConfigEnableParallelHash::VALUES, |v| { + serde_json::from_value::(v).map(|e| e.to_string()) + }), + (PgConfigIoMethod::VALUES, |v| { + serde_json::from_value::(v).map(|e| e.to_string()) + }), + (PgConfigLogLockWaits::VALUES, |v| { + serde_json::from_value::(v).map(|e| e.to_string()) + }), + ] { + for value in values { + assert_eq!(parse(serde_json::json!(value)).unwrap(), *value); + } + } + let future: PgConfigIoMethod = serde_json::from_value(serde_json::json!("posix_aio")).unwrap(); + assert_eq!(future, PgConfigIoMethod::Unknown("posix_aio".into())); +} diff --git a/crates/clickhouse-openapi-analyzer/src/config.rs b/crates/clickhouse-openapi-analyzer/src/config.rs index 99fbf573f..81c01d8ea 100644 --- a/crates/clickhouse-openapi-analyzer/src/config.rs +++ b/crates/clickhouse-openapi-analyzer/src/config.rs @@ -244,19 +244,38 @@ const OPTIONALITY_EXEMPTIONS: &[(&str, &str)] = &[ ("PgConfig", "autovacuum_vacuum_scale_factor"), ("PgConfig", "autovacuum_vacuum_threshold"), ("PgConfig", "autovacuum_work_mem"), + ("PgConfig", "bgwriter_delay"), + ("PgConfig", "bgwriter_flush_after"), + ("PgConfig", "bgwriter_lru_maxpages"), + ("PgConfig", "bgwriter_lru_multiplier"), ("PgConfig", "checkpoint_timeout"), + ("PgConfig", "default_toast_compression"), ("PgConfig", "default_transaction_isolation"), ("PgConfig", "effective_cache_size"), ("PgConfig", "effective_io_concurrency"), + ("PgConfig", "enable_parallel_hash"), + ("PgConfig", "hash_mem_multiplier"), ("PgConfig", "idle_in_transaction_session_timeout"), ("PgConfig", "idle_session_timeout"), + ("PgConfig", "io_method"), + ("PgConfig", "io_workers"), ("PgConfig", "lock_timeout"), + ("PgConfig", "log_lock_waits"), + ("PgConfig", "log_min_duration_statement"), + ("PgConfig", "log_parameter_max_length"), + ("PgConfig", "log_temp_files"), + ("PgConfig", "logical_decoding_work_mem"), + ("PgConfig", "maintenance_io_concurrency"), ("PgConfig", "maintenance_work_mem"), ("PgConfig", "max_connections"), + ("PgConfig", "max_logical_replication_workers"), ("PgConfig", "max_parallel_maintenance_workers"), ("PgConfig", "max_parallel_workers"), ("PgConfig", "max_parallel_workers_per_gather"), + ("PgConfig", "max_replication_slots"), ("PgConfig", "max_slot_wal_keep_size"), + ("PgConfig", "max_sync_workers_per_subscription"), + ("PgConfig", "max_wal_senders"), ("PgConfig", "max_wal_size"), ("PgConfig", "max_worker_processes"), ("PgConfig", "min_wal_size"), @@ -264,7 +283,10 @@ const OPTIONALITY_EXEMPTIONS: &[(&str, &str)] = &[ ("PgConfig", "shared_memory_percent"), ("PgConfig", "ssl_min_protocol_version"), ("PgConfig", "statement_timeout"), + ("PgConfig", "temp_buffers"), + ("PgConfig", "track_activity_query_size"), ("PgConfig", "transaction_timeout"), + ("PgConfig", "vacuum_cost_delay"), ("PgConfig", "wal_buffers"), ("PgConfig", "wal_compression"), ("PgConfig", "wal_keep_size"), diff --git a/crates/clickhousectl/src/cloud/postgres.rs b/crates/clickhousectl/src/cloud/postgres.rs index 8c0b7a56d..68839cada 100644 --- a/crates/clickhousectl/src/cloud/postgres.rs +++ b/crates/clickhousectl/src/cloud/postgres.rs @@ -1996,6 +1996,7 @@ pub async fn postgres_create( tags, pg_config, pg_bouncer_config, + ip_access_list: None, }; let org_id = resolve_org_id(client).await?; @@ -2047,6 +2048,7 @@ fn build_postgres_update_request( size, ha_type, tags, + ip_access_list: None, } } diff --git a/crates/clickhousectl/tests/cli_request_shape_test.rs b/crates/clickhousectl/tests/cli_request_shape_test.rs index 70bf81229..8413d378e 100644 --- a/crates/clickhousectl/tests/cli_request_shape_test.rs +++ b/crates/clickhousectl/tests/cli_request_shape_test.rs @@ -19967,6 +19967,68 @@ async fn pgbouncer_file_inputs_preserve_string_maps_in_every_write_path() { } } +// Omitting `ipAccessList` on create means "allow all"; an empty list blocks +// public access, so the CLI must not send the key until it exposes it. +#[tokio::test] +async fn postgres_create_and_update_omit_the_ip_access_list() { + for (args, verb, endpoint) in [ + ( + vec![ + "postgres", + "create", + "--name", + "pg-test", + "--region", + "us-east-1", + "--size", + "c6gd.large", + ], + "POST", + "/v1/organizations/org-1/postgres", + ), + ( + vec!["postgres", "update", "pg-1", "--size", "c6gd.xlarge"], + "PATCH", + "/v1/organizations/org-1/postgres/pg-1", + ), + ] { + let mock = MockServer::start().await; + Mock::given(method(verb)) + .and(path(endpoint)) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "result": {"id": "11111111-1111-1111-1111-111111111111", "name": "pg-test"}, + "status": 200, + }))) + .expect(1) + .mount(&mock) + .await; + Mock::given(method("GET")) + .and(path("/v1/organizations/org-1/postgres/pg-1")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "result": { + "id": "11111111-1111-1111-1111-111111111111", + "tags": [], + "ipAccessList": [{"id": "rule-1", "source": "0.0.0.0/0", "portRange": "5432"}] + }, + "status": 200, + }))) + .mount(&mock) + .await; + let mut args = args; + args.extend(["--org-id", "org-1"]); + let output = invoke_pgbouncer_cli(&mock, &args); + assert_success(&output); + let requests = mock.received_requests().await.unwrap(); + let writes: Vec<_> = requests + .iter() + .filter(|request| request.method.as_str() == verb) + .collect(); + assert_eq!(writes.len(), 1, "{args:?}"); + let body: Value = serde_json::from_slice(&writes[0].body).unwrap(); + assert!(body.get("ipAccessList").is_none(), "{args:?}: {body}"); + } +} + #[tokio::test] async fn pgbouncer_invalid_file_values_fail_before_any_api_request() { let directory = tempfile::tempdir().unwrap();