From e31d73a1eb41abf08389bc9629ab9b7e98259e2c Mon Sep 17 00:00:00 2001 From: Ernesto Ongaro Date: Tue, 15 Sep 2026 07:39:05 +0100 Subject: [PATCH 1/2] Sync spec: list pagination and sort params, color palettes, skills Headline (exploreomni/omni#64034): documents, folders and models list endpoints now document the params their validators enforce. pageSize is an integer 1-100 (default 20), sortDirection defaults to desc, documents gains sortField lastViewedAt, folders drops createdAt/updatedAt from sortField, and models gains modelKind EXTENSION_BRANCH. Also pulled in by the full sync: - New color-palettes group: list, get, create, update, delete - New skills group: list, get, create, update, delete - models content-validator-get: --force-full-validation - query wait: deprecated job_ids param, exposed as --job-ids-2 - query run body: timezone - Response/schema updates for routines, v2 document app writes, documents, topics and whoami Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_0174fd5xx9JyExdkPYJXECu4 --- api/openapi.json | 598 ++++++++++++++++++++++++++++++++++++++++-- cmd/omni/openapi.json | 598 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 1144 insertions(+), 52 deletions(-) diff --git a/api/openapi.json b/api/openapi.json index 9189eca..a543c3e 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -31,6 +31,10 @@ "description": "API token management", "name": "API Tokens" }, + { + "description": "Custom chart color palette management", + "name": "Color Palettes" + }, { "description": "Database connections and environments", "name": "Connections" @@ -3398,6 +3402,315 @@ "success" ] }, + "ColorPalettesListResponse": { + "type": "object", + "properties": { + "color_palettes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered colors as stored, usually hex values", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was created" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "Palette ID. Visualizations reference the palette by this value." + }, + "name": { + "type": "string", + "description": "Palette name", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was last updated" + } + }, + "required": [ + "colors", + "created_at", + "id", + "name", + "type", + "updated_at" + ] + }, + "description": "Custom palettes, sorted by name" + } + }, + "required": [ + "color_palettes" + ] + }, + "ColorPalettesCreateResponse": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered colors as stored, usually hex values", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was created" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "Palette ID. Visualizations reference the palette by this value." + }, + "name": { + "type": "string", + "description": "Palette name", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was last updated" + } + }, + "required": [ + "colors", + "created_at", + "id", + "name", + "type", + "updated_at" + ] + }, + "ColorPalettesCreateBody": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$", + "example": "#1f77b4" + }, + "minItems": 1, + "maxItems": 50, + "description": "Ordered hex colors. Discrete palettes are used in order; continuous palettes interpolate between them.", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Palette name, unique per type within the organization", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + } + }, + "required": [ + "colors", + "name", + "type" + ], + "additionalProperties": false + }, + "ColorPalettesGetResponse": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered colors as stored, usually hex values", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was created" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "Palette ID. Visualizations reference the palette by this value." + }, + "name": { + "type": "string", + "description": "Palette name", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was last updated" + } + }, + "required": [ + "colors", + "created_at", + "id", + "name", + "type", + "updated_at" + ] + }, + "ColorPalettesUpdateResponse": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered colors as stored, usually hex values", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was created" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "Palette ID. Visualizations reference the palette by this value." + }, + "name": { + "type": "string", + "description": "Palette name", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was last updated" + } + }, + "required": [ + "colors", + "created_at", + "id", + "name", + "type", + "updated_at" + ] + }, + "ColorPalettesUpdateBody": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$", + "example": "#1f77b4" + }, + "minItems": 1, + "maxItems": 50, + "description": "Ordered hex colors. Discrete palettes are used in order; continuous palettes interpolate between them.", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Palette name, unique per type within the organization", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + } + }, + "additionalProperties": false + }, "ConnectionsDbtRotateSigningKeyResponse": { "type": "object", "properties": { @@ -34821,7 +35134,8 @@ "type": [ "string", "null" - ] + ], + "maxLength": 1024 }, "name": { "type": "string", @@ -40395,6 +40709,14 @@ "type": "integer", "description": "Number of workbook models replaced" }, + "skipped_foreign_homed_not_in_branch_count": { + "type": "integer", + "description": "Number of tabs of documents homed in other models that were skipped because a replace on a branch has no branch of those models to draft on" + }, + "skipped_foreign_homed_unauthorized_count": { + "type": "integer", + "description": "Number of tabs of documents homed in other models that were skipped because the caller cannot update those models" + }, "skipped_foreign_tabs_count": { "type": "integer", "description": "Number of tabs left alone because they query a shared model other than this one" @@ -45248,7 +45570,7 @@ } }, "403": { - "description": "AI routines or AI query generation are not enabled for the organization, the API key cannot act on behalf of the requested user, the acting user lacks routines access (the Use routines role permission) on the routine model, the request supplied a `condition` but conditional routines are not enabled for the organization, or the target user is an embed user (embed users cannot own routines).", + "description": "AI routines or AI query generation are not enabled for the organization, the API key cannot act on behalf of the requested user, the acting user lacks routines access (the Use routines role permission) on the routine model, the request supplied a `condition` but conditional routines are not enabled for the organization, or the target user is an embed user in an organization that has not enabled routines for embed users.", "content": { "application/json": { "schema": { @@ -45923,6 +46245,219 @@ } } }, + "/api/v1/color-palettes": { + "get": { + "description": "Lists the custom chart color palettes defined for the organization. Built-in palettes are not included.", + "operationId": "colorPalettesList", + "summary": "List custom color palettes", + "tags": [ + "Color Palettes" + ], + "responses": { + "200": { + "description": "Custom color palettes in the organization", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesListResponse" + } + } + } + }, + "401": { + "description": "Authentication required" + } + } + }, + "post": { + "description": "Creates a custom chart color palette. Requires the Manage Config permission. Palette names must be unique per type within the organization.", + "operationId": "colorPalettesCreate", + "summary": "Create a custom color palette", + "tags": [ + "Color Palettes" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesCreateBody" + } + } + } + }, + "responses": { + "201": { + "description": "Color palette created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesCreateResponse" + } + } + } + }, + "400": { + "description": "Invalid request body" + }, + "401": { + "description": "Authentication required" + }, + "403": { + "description": "Requires the Manage Config permission" + }, + "409": { + "description": "A palette with this name and type already exists" + } + } + } + }, + "/api/v1/color-palettes/{id}": { + "get": { + "description": "Returns one custom chart color palette by ID.", + "operationId": "colorPalettesGet", + "summary": "Get a custom color palette", + "tags": [ + "Color Palettes" + ], + "parameters": [ + { + "schema": { + "type": "string", + "format": "uuid", + "description": "Palette ID", + "example": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "required": true, + "description": "Palette ID", + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Color palette details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesGetResponse" + } + } + } + }, + "400": { + "description": "Invalid palette ID" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Color palette not found" + } + } + }, + "put": { + "description": "Updates a custom chart color palette. Omitted fields keep their current values. Charts using the palette pick up the new colors. Requires the Manage Config permission.", + "operationId": "colorPalettesUpdate", + "summary": "Update a custom color palette", + "tags": [ + "Color Palettes" + ], + "parameters": [ + { + "schema": { + "type": "string", + "format": "uuid", + "description": "Palette ID", + "example": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "required": true, + "description": "Palette ID", + "name": "id", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesUpdateBody" + } + } + } + }, + "responses": { + "200": { + "description": "Color palette updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesUpdateResponse" + } + } + } + }, + "400": { + "description": "Invalid request body or palette ID" + }, + "401": { + "description": "Authentication required" + }, + "403": { + "description": "Requires the Manage Config permission" + }, + "404": { + "description": "Color palette not found" + }, + "409": { + "description": "Another palette already has this name and type, or the palette is the organization's default and cannot change type" + } + } + }, + "delete": { + "description": "Deletes a custom chart color palette. Charts using it fall back to the organization's default palette. The organization's current default palette cannot be deleted. Requires the Manage Config permission.", + "operationId": "colorPalettesDelete", + "summary": "Delete a custom color palette", + "tags": [ + "Color Palettes" + ], + "parameters": [ + { + "schema": { + "type": "string", + "format": "uuid", + "description": "Palette ID", + "example": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "required": true, + "description": "Palette ID", + "name": "id", + "in": "path" + } + ], + "responses": { + "204": { + "description": "Color palette deleted" + }, + "400": { + "description": "Invalid palette ID" + }, + "401": { + "description": "Authentication required" + }, + "403": { + "description": "Requires the Manage Config permission" + }, + "404": { + "description": "Color palette not found" + }, + "409": { + "description": "The palette is the organization's default and cannot be deleted" + } + } + } + }, "/api/v1/connections": { "get": { "operationId": "connectionsList", @@ -50102,12 +50637,14 @@ { "schema": { "type": "integer", - "exclusiveMinimum": 0, - "default": 50, - "description": "Number of records per page" + "minimum": 1, + "maximum": 100, + "default": 20, + "description": "Number of results per page (1-100, integer)", + "example": 20 }, "required": false, - "description": "Number of records per page", + "description": "Number of results per page (1-100, integer)", "name": "pageSize", "in": "query" }, @@ -50118,11 +50655,12 @@ "asc", "desc" ], - "default": "asc", - "description": "Sort direction" + "default": "desc", + "description": "Sort direction for results", + "example": "desc" }, "required": false, - "description": "Sort direction", + "description": "Sort direction for results", "name": "sortDirection", "in": "query" }, @@ -50133,6 +50671,7 @@ "name", "favorites", "updatedAt", + "lastViewedAt", "visits" ], "default": "name", @@ -53770,15 +54309,15 @@ }, { "schema": { - "type": [ - "number", - "null" - ], - "description": "Number of results per page", + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 20, + "description": "Number of results per page (1-100, integer)", "example": 20 }, "required": false, - "description": "Number of results per page", + "description": "Number of results per page (1-100, integer)", "name": "pageSize", "in": "query" }, @@ -53813,10 +54352,12 @@ "asc", "desc" ], - "description": "Sort direction" + "default": "desc", + "description": "Sort direction for results", + "example": "desc" }, "required": false, - "description": "Sort direction", + "description": "Sort direction for results", "name": "sortDirection", "in": "query" }, @@ -53825,11 +54366,10 @@ "type": "string", "enum": [ "name", - "createdAt", - "updatedAt", "favorites", "path" ], + "default": "name", "description": "Field to sort by" }, "required": false, @@ -55232,11 +55772,11 @@ { "schema": { "type": "string", - "description": "Comma-separated list of fields to include (e.g., activeBranches)", + "description": "Comma-separated list of fields to include. Supported: activeBranches", "example": "activeBranches" }, "required": false, - "description": "Comma-separated list of fields to include (e.g., activeBranches)", + "description": "Comma-separated list of fields to include. Supported: activeBranches", "name": "include", "in": "query" }, @@ -55275,6 +55815,7 @@ "SHARED", "SHARED_EXTENSION", "BRANCH", + "EXTENSION_BRANCH", "WORKBOOK", "QUERY" ], @@ -55298,12 +55839,14 @@ { "schema": { "type": "integer", - "exclusiveMinimum": 0, - "description": "Number of results per page", + "minimum": 1, + "maximum": 100, + "default": 20, + "description": "Number of results per page (1-100, integer)", "example": 20 }, "required": false, - "description": "Number of results per page", + "description": "Number of results per page (1-100, integer)", "name": "pageSize", "in": "query" }, @@ -55314,10 +55857,12 @@ "asc", "desc" ], - "description": "Sort direction" + "default": "desc", + "description": "Sort direction for results", + "example": "desc" }, "required": false, - "description": "Sort direction", + "description": "Sort direction for results", "name": "sortDirection", "in": "query" }, @@ -55332,6 +55877,7 @@ "createdAt", "updatedAt" ], + "default": "updatedAt", "description": "Field to sort by" }, "required": false, diff --git a/cmd/omni/openapi.json b/cmd/omni/openapi.json index 9189eca..a543c3e 100644 --- a/cmd/omni/openapi.json +++ b/cmd/omni/openapi.json @@ -31,6 +31,10 @@ "description": "API token management", "name": "API Tokens" }, + { + "description": "Custom chart color palette management", + "name": "Color Palettes" + }, { "description": "Database connections and environments", "name": "Connections" @@ -3398,6 +3402,315 @@ "success" ] }, + "ColorPalettesListResponse": { + "type": "object", + "properties": { + "color_palettes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered colors as stored, usually hex values", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was created" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "Palette ID. Visualizations reference the palette by this value." + }, + "name": { + "type": "string", + "description": "Palette name", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was last updated" + } + }, + "required": [ + "colors", + "created_at", + "id", + "name", + "type", + "updated_at" + ] + }, + "description": "Custom palettes, sorted by name" + } + }, + "required": [ + "color_palettes" + ] + }, + "ColorPalettesCreateResponse": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered colors as stored, usually hex values", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was created" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "Palette ID. Visualizations reference the palette by this value." + }, + "name": { + "type": "string", + "description": "Palette name", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was last updated" + } + }, + "required": [ + "colors", + "created_at", + "id", + "name", + "type", + "updated_at" + ] + }, + "ColorPalettesCreateBody": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$", + "example": "#1f77b4" + }, + "minItems": 1, + "maxItems": 50, + "description": "Ordered hex colors. Discrete palettes are used in order; continuous palettes interpolate between them.", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Palette name, unique per type within the organization", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + } + }, + "required": [ + "colors", + "name", + "type" + ], + "additionalProperties": false + }, + "ColorPalettesGetResponse": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered colors as stored, usually hex values", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was created" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "Palette ID. Visualizations reference the palette by this value." + }, + "name": { + "type": "string", + "description": "Palette name", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was last updated" + } + }, + "required": [ + "colors", + "created_at", + "id", + "name", + "type", + "updated_at" + ] + }, + "ColorPalettesUpdateResponse": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered colors as stored, usually hex values", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was created" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "Palette ID. Visualizations reference the palette by this value." + }, + "name": { + "type": "string", + "description": "Palette name", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the palette was last updated" + } + }, + "required": [ + "colors", + "created_at", + "id", + "name", + "type", + "updated_at" + ] + }, + "ColorPalettesUpdateBody": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": { + "type": "string", + "pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$", + "example": "#1f77b4" + }, + "minItems": 1, + "maxItems": 50, + "description": "Ordered hex colors. Discrete palettes are used in order; continuous palettes interpolate between them.", + "example": [ + "#1f77b4", + "#ff7f0e", + "#2ca02c" + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Palette name, unique per type within the organization", + "example": "Brand colors" + }, + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ], + "description": "'discrete' palettes color categories; 'continuous' palettes define a gradient for numeric scales", + "example": "discrete" + } + }, + "additionalProperties": false + }, "ConnectionsDbtRotateSigningKeyResponse": { "type": "object", "properties": { @@ -34821,7 +35134,8 @@ "type": [ "string", "null" - ] + ], + "maxLength": 1024 }, "name": { "type": "string", @@ -40395,6 +40709,14 @@ "type": "integer", "description": "Number of workbook models replaced" }, + "skipped_foreign_homed_not_in_branch_count": { + "type": "integer", + "description": "Number of tabs of documents homed in other models that were skipped because a replace on a branch has no branch of those models to draft on" + }, + "skipped_foreign_homed_unauthorized_count": { + "type": "integer", + "description": "Number of tabs of documents homed in other models that were skipped because the caller cannot update those models" + }, "skipped_foreign_tabs_count": { "type": "integer", "description": "Number of tabs left alone because they query a shared model other than this one" @@ -45248,7 +45570,7 @@ } }, "403": { - "description": "AI routines or AI query generation are not enabled for the organization, the API key cannot act on behalf of the requested user, the acting user lacks routines access (the Use routines role permission) on the routine model, the request supplied a `condition` but conditional routines are not enabled for the organization, or the target user is an embed user (embed users cannot own routines).", + "description": "AI routines or AI query generation are not enabled for the organization, the API key cannot act on behalf of the requested user, the acting user lacks routines access (the Use routines role permission) on the routine model, the request supplied a `condition` but conditional routines are not enabled for the organization, or the target user is an embed user in an organization that has not enabled routines for embed users.", "content": { "application/json": { "schema": { @@ -45923,6 +46245,219 @@ } } }, + "/api/v1/color-palettes": { + "get": { + "description": "Lists the custom chart color palettes defined for the organization. Built-in palettes are not included.", + "operationId": "colorPalettesList", + "summary": "List custom color palettes", + "tags": [ + "Color Palettes" + ], + "responses": { + "200": { + "description": "Custom color palettes in the organization", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesListResponse" + } + } + } + }, + "401": { + "description": "Authentication required" + } + } + }, + "post": { + "description": "Creates a custom chart color palette. Requires the Manage Config permission. Palette names must be unique per type within the organization.", + "operationId": "colorPalettesCreate", + "summary": "Create a custom color palette", + "tags": [ + "Color Palettes" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesCreateBody" + } + } + } + }, + "responses": { + "201": { + "description": "Color palette created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesCreateResponse" + } + } + } + }, + "400": { + "description": "Invalid request body" + }, + "401": { + "description": "Authentication required" + }, + "403": { + "description": "Requires the Manage Config permission" + }, + "409": { + "description": "A palette with this name and type already exists" + } + } + } + }, + "/api/v1/color-palettes/{id}": { + "get": { + "description": "Returns one custom chart color palette by ID.", + "operationId": "colorPalettesGet", + "summary": "Get a custom color palette", + "tags": [ + "Color Palettes" + ], + "parameters": [ + { + "schema": { + "type": "string", + "format": "uuid", + "description": "Palette ID", + "example": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "required": true, + "description": "Palette ID", + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Color palette details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesGetResponse" + } + } + } + }, + "400": { + "description": "Invalid palette ID" + }, + "401": { + "description": "Authentication required" + }, + "404": { + "description": "Color palette not found" + } + } + }, + "put": { + "description": "Updates a custom chart color palette. Omitted fields keep their current values. Charts using the palette pick up the new colors. Requires the Manage Config permission.", + "operationId": "colorPalettesUpdate", + "summary": "Update a custom color palette", + "tags": [ + "Color Palettes" + ], + "parameters": [ + { + "schema": { + "type": "string", + "format": "uuid", + "description": "Palette ID", + "example": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "required": true, + "description": "Palette ID", + "name": "id", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesUpdateBody" + } + } + } + }, + "responses": { + "200": { + "description": "Color palette updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ColorPalettesUpdateResponse" + } + } + } + }, + "400": { + "description": "Invalid request body or palette ID" + }, + "401": { + "description": "Authentication required" + }, + "403": { + "description": "Requires the Manage Config permission" + }, + "404": { + "description": "Color palette not found" + }, + "409": { + "description": "Another palette already has this name and type, or the palette is the organization's default and cannot change type" + } + } + }, + "delete": { + "description": "Deletes a custom chart color palette. Charts using it fall back to the organization's default palette. The organization's current default palette cannot be deleted. Requires the Manage Config permission.", + "operationId": "colorPalettesDelete", + "summary": "Delete a custom color palette", + "tags": [ + "Color Palettes" + ], + "parameters": [ + { + "schema": { + "type": "string", + "format": "uuid", + "description": "Palette ID", + "example": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "required": true, + "description": "Palette ID", + "name": "id", + "in": "path" + } + ], + "responses": { + "204": { + "description": "Color palette deleted" + }, + "400": { + "description": "Invalid palette ID" + }, + "401": { + "description": "Authentication required" + }, + "403": { + "description": "Requires the Manage Config permission" + }, + "404": { + "description": "Color palette not found" + }, + "409": { + "description": "The palette is the organization's default and cannot be deleted" + } + } + } + }, "/api/v1/connections": { "get": { "operationId": "connectionsList", @@ -50102,12 +50637,14 @@ { "schema": { "type": "integer", - "exclusiveMinimum": 0, - "default": 50, - "description": "Number of records per page" + "minimum": 1, + "maximum": 100, + "default": 20, + "description": "Number of results per page (1-100, integer)", + "example": 20 }, "required": false, - "description": "Number of records per page", + "description": "Number of results per page (1-100, integer)", "name": "pageSize", "in": "query" }, @@ -50118,11 +50655,12 @@ "asc", "desc" ], - "default": "asc", - "description": "Sort direction" + "default": "desc", + "description": "Sort direction for results", + "example": "desc" }, "required": false, - "description": "Sort direction", + "description": "Sort direction for results", "name": "sortDirection", "in": "query" }, @@ -50133,6 +50671,7 @@ "name", "favorites", "updatedAt", + "lastViewedAt", "visits" ], "default": "name", @@ -53770,15 +54309,15 @@ }, { "schema": { - "type": [ - "number", - "null" - ], - "description": "Number of results per page", + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 20, + "description": "Number of results per page (1-100, integer)", "example": 20 }, "required": false, - "description": "Number of results per page", + "description": "Number of results per page (1-100, integer)", "name": "pageSize", "in": "query" }, @@ -53813,10 +54352,12 @@ "asc", "desc" ], - "description": "Sort direction" + "default": "desc", + "description": "Sort direction for results", + "example": "desc" }, "required": false, - "description": "Sort direction", + "description": "Sort direction for results", "name": "sortDirection", "in": "query" }, @@ -53825,11 +54366,10 @@ "type": "string", "enum": [ "name", - "createdAt", - "updatedAt", "favorites", "path" ], + "default": "name", "description": "Field to sort by" }, "required": false, @@ -55232,11 +55772,11 @@ { "schema": { "type": "string", - "description": "Comma-separated list of fields to include (e.g., activeBranches)", + "description": "Comma-separated list of fields to include. Supported: activeBranches", "example": "activeBranches" }, "required": false, - "description": "Comma-separated list of fields to include (e.g., activeBranches)", + "description": "Comma-separated list of fields to include. Supported: activeBranches", "name": "include", "in": "query" }, @@ -55275,6 +55815,7 @@ "SHARED", "SHARED_EXTENSION", "BRANCH", + "EXTENSION_BRANCH", "WORKBOOK", "QUERY" ], @@ -55298,12 +55839,14 @@ { "schema": { "type": "integer", - "exclusiveMinimum": 0, - "description": "Number of results per page", + "minimum": 1, + "maximum": 100, + "default": 20, + "description": "Number of results per page (1-100, integer)", "example": 20 }, "required": false, - "description": "Number of results per page", + "description": "Number of results per page (1-100, integer)", "name": "pageSize", "in": "query" }, @@ -55314,10 +55857,12 @@ "asc", "desc" ], - "description": "Sort direction" + "default": "desc", + "description": "Sort direction for results", + "example": "desc" }, "required": false, - "description": "Sort direction", + "description": "Sort direction for results", "name": "sortDirection", "in": "query" }, @@ -55332,6 +55877,7 @@ "createdAt", "updatedAt" ], + "default": "updatedAt", "description": "Field to sort by" }, "required": false, From 9e4d7f922ecf63bf6a1e22458e2e92a7f747e08b Mon Sep 17 00:00:00 2001 From: Ernesto Ongaro Date: Tue, 15 Sep 2026 07:54:30 +0100 Subject: [PATCH 2/2] Keep spec backticks from becoming flag placeholders pflag reads the first backquoted word in a flag's usage as its value placeholder, so a spec description mentioning `job_ids` rendered as --job-ids job_ids. Generated flag descriptions (query params, multipart fields, promoted body fields) now quote code spans with ' instead, and every flag shows its type. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_0174fd5xx9JyExdkPYJXECu4 --- internal/openapi/body_shorthand.go | 2 +- internal/openapi/generate.go | 9 +- internal/openapi/generate_test.go | 127 +++++++++++++++++++++++++++++ internal/openapi/multipart.go | 2 +- internal/openapi/multipart_test.go | 12 +++ 5 files changed, 149 insertions(+), 3 deletions(-) diff --git a/internal/openapi/body_shorthand.go b/internal/openapi/body_shorthand.go index 4c027b6..e3581b1 100644 --- a/internal/openapi/body_shorthand.go +++ b/internal/openapi/body_shorthand.go @@ -306,7 +306,7 @@ func applyBodyShorthand(cmd *cobra.Command, op *operationInfo, sh *BodyShorthand if err != nil { fieldType = "string" } - cmd.Flags().Var(&shorthandFlagValue{value: f.Default, typeName: fieldType}, f.FlagName, f.Description+bodyExclusiveSuffix) + cmd.Flags().Var(&shorthandFlagValue{value: f.Default, typeName: fieldType}, f.FlagName, flagUsage(f.Description+bodyExclusiveSuffix)) } // Say the same thing from the other side: --body's own description on a diff --git a/internal/openapi/generate.go b/internal/openapi/generate.go index 194a7f3..7095409 100644 --- a/internal/openapi/generate.go +++ b/internal/openapi/generate.go @@ -482,7 +482,7 @@ func buildCommand(op *operationInfo, exec Executor) *cobra.Command { if qf.Param.Required { desc = strings.TrimSpace(desc + " (required)") } - cmd.Flags().String(qf.Name, "", desc) + cmd.Flags().String(qf.Name, "", flagUsage(desc)) if qf.Param.Required { cmd.MarkFlagRequired(qf.Name) hasRequiredQuery = true @@ -613,6 +613,13 @@ func firstLine(s string) string { return strings.TrimSpace(s) } +// flagUsage quotes a description's `code` spans with ' instead: pflag reads the +// first backquoted word in a flag's usage as its value placeholder, so a spec +// description mentioning `job_ids` would print as --job-ids job_ids. +func flagUsage(desc string) string { + return strings.ReplaceAll(desc, "`", "'") +} + // queryFlagName is the generic escape hatch flag for query params the spec // doesn't declare. const queryFlagName = "query" diff --git a/internal/openapi/generate_test.go b/internal/openapi/generate_test.go index 37b40c0..af89260 100644 --- a/internal/openapi/generate_test.go +++ b/internal/openapi/generate_test.go @@ -2197,3 +2197,130 @@ func TestBodyDeclares(t *testing.T) { t.Error("models list does not return a stream") } } + +// pflag takes the first backquoted word in a flag's usage as its value +// placeholder, so a spec description mentioning `job_ids` must not print as +// --job-ids job_ids. +func TestBuildCommand_BackticksInDescriptionKeepTypePlaceholder(t *testing.T) { + op := &operationInfo{ + Tag: "test", + OperationID: "testWait", + Method: "GET", + Path: "/api/v1/wait", + QueryParams: []paramInfo{ + {Name: "jobIds", In: "query", Description: "Job IDs. Required unless the deprecated `job_ids` is sent."}, + {Name: "mode", In: "query", Description: "Use `fast` or `slow`.", Enum: []string{"fast", "slow"}, Required: true}, + }, + } + cmd := buildCommand(op, func(APIRequest) error { return nil }) + + for flag, want := range map[string]string{ + "job-ids": "Job IDs. Required unless the deprecated 'job_ids' is sent.", + "mode": "Use 'fast' or 'slow'. [fast, slow] (required)", + } { + f := cmd.Flags().Lookup(flag) + if f == nil { + t.Fatalf("missing --%s", flag) + } + name, usage := pflag.UnquoteUsage(f) + if name != "string" { + t.Errorf("--%s placeholder = %q, want \"string\"", flag, name) + } + if usage != want { + t.Errorf("--%s usage = %q, want %q", flag, usage, want) + } + } + + var help bytes.Buffer + cmd.SetOut(&help) + cmd.SetArgs([]string{"--help"}) + if err := cmd.Execute(); err != nil { + t.Fatalf("--help: %v", err) + } + if !regexp.MustCompile(`--job-ids string\s+Job IDs`).MatchString(help.String()) { + t.Errorf("help should show --job-ids string:\n%s", help.String()) + } +} + +// Every flag generated from the real spec shows its type as the placeholder, +// never a word lifted from its description. +func TestGenerateCommands_NoFlagPlaceholderFromDescription(t *testing.T) { + specData := loadSpec(t) + cmds, err := GenerateCommands(specData, func(APIRequest) error { return nil }) + if err != nil { + t.Fatalf("GenerateCommands: %v", err) + } + checked, quoted := 0, 0 + var walk func(*cobra.Command) + walk = func(c *cobra.Command) { + c.Flags().VisitAll(func(f *pflag.Flag) { + checked++ + if strings.Contains(f.Usage, "`") { + t.Errorf("%s --%s usage still has a backtick: %q", c.CommandPath(), f.Name, f.Usage) + } + if strings.Contains(f.Usage, "'") { + quoted++ + } + name, _ := pflag.UnquoteUsage(f) + typeName, _ := pflag.UnquoteUsage(&pflag.Flag{Value: f.Value}) + if name != typeName { + t.Errorf("%s --%s placeholder = %q, want its type %q", c.CommandPath(), f.Name, name, typeName) + } + }) + for _, sub := range c.Commands() { + walk(sub) + } + } + for _, c := range cmds { + walk(c) + } + if checked == 0 { + t.Fatal("no flags checked") + } + // The spec does use backticks in param descriptions; if none survive as + // quotes, the check above proved nothing. + if quoted == 0 { + t.Error("expected some flag descriptions with quoted code spans") + } +} + +// The commands that showed a description word as their placeholder. +func TestGenerateCommands_KnownBacktickFlags(t *testing.T) { + specData := loadSpec(t) + cmds, err := GenerateCommands(specData, func(APIRequest) error { return nil }) + if err != nil { + t.Fatalf("GenerateCommands: %v", err) + } + find := func(path ...string) *cobra.Command { + for _, c := range cmds { + if c.Name() != path[0] { + continue + } + sub, _, err := c.Find(path[1:]) + if err == nil && sub != c { + return sub + } + } + t.Fatalf("command %v not found", path) + return nil + } + for _, tc := range []struct { + path []string + flag string + want string + }{ + {[]string{"query", "wait"}, "job-ids", "deprecated 'job_ids' is sent"}, + {[]string{"skills", "list"}, "creator-id", "'creator.id' from a listed skill"}, + {[]string{"models", "refresh"}, "hard-refresh", "'tables' filters"}, + {[]string{"whoami", "whoami"}, "model-id", "'rolesByModelTruncated'"}, + } { + f := find(tc.path...).Flags().Lookup(tc.flag) + if f == nil { + t.Errorf("%v: missing --%s", tc.path, tc.flag) + continue + } + if name, usage := pflag.UnquoteUsage(f); name != "string" || !strings.Contains(usage, tc.want) { + t.Errorf("%v --%s: placeholder %q, usage %q", tc.path, tc.flag, name, usage) + } + } +} diff --git a/internal/openapi/multipart.go b/internal/openapi/multipart.go index 5ed77ee..4d0f82a 100644 --- a/internal/openapi/multipart.go +++ b/internal/openapi/multipart.go @@ -133,7 +133,7 @@ func registerMultipartFlags(cmd *cobra.Command, fields []multipartFieldInfo) { if field.Required { description += " [required unless supplied via --body]" } - cmd.Flags().String(flagName, "", description) + cmd.Flags().String(flagName, "", flagUsage(description)) } } diff --git a/internal/openapi/multipart_test.go b/internal/openapi/multipart_test.go index 697251f..eb13200 100644 --- a/internal/openapi/multipart_test.go +++ b/internal/openapi/multipart_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) const multipartTestSpec = `{ @@ -427,3 +428,14 @@ func TestRegisterMultipartFlags_ResolvesCollisions(t *testing.T) { } } } + +func TestRegisterMultipartFlags_BackticksKeepTypePlaceholder(t *testing.T) { + cmd := &cobra.Command{Use: "upload"} + registerMultipartFlags(cmd, []multipartFieldInfo{ + {Name: "file", FlagName: "file", Description: "CSV to upload; see `format`", Binary: true, Required: true}, + }) + name, usage := pflag.UnquoteUsage(cmd.Flags().Lookup("file")) + if name != "string" || usage != "CSV to upload; see 'format' (file path) [required unless supplied via --body]" { + t.Errorf("placeholder %q, usage %q", name, usage) + } +}