Skip to content

Commit 86eb885

Browse files
Al-Pragliolaclaude
andauthored
refactor(catalog): split OpenAPI spec into per-plugin files with independent generation (#2735)
* feat(catalog): split OpenAPI spec into per-plugin files Restructure the monolithic catalog.yaml source into per-plugin specs so each plugin can independently define its own API paths and schemas while sharing common types from common.yaml. - Slim api/openapi/src/catalog.yaml to core (sources, labels, preview) - Add catalog/plugins/model/api/openapi/ (model paths + schemas) - Add catalog/plugins/mcp/api/openapi/ (MCP paths + schemas) - Add scripts/merge_catalog_specs.sh to combine core + plugins + lib - Update Makefile to use merge_catalog_specs.sh for catalog.yaml The merged output is semantically identical to the previous monolithic spec — generated code, validation, and tests are unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * feat(catalog): per-plugin server stub generation Generate server stubs (controllers, interfaces) per-plugin instead of from the unified spec, while keeping client models in catalog/pkg/openapi/. - Add scripts/assemble_plugin_spec.sh to build standalone specs per plugin - Add catalog/plugins/{model,mcp}/scripts/gen_openapi_server.sh for independent per-plugin code generation - Add per-plugin .openapi-generator-ignore files - Split api.go into api_model.go + api_mcp.go (same package, no breaking change) - Refactor catalog/scripts/gen_openapi_server.sh into orchestrator - Update catalog/Makefile with per-plugin targets and dependency isolation Changing a plugin's spec only regenerates that plugin's controller. Generated output is identical to the previous unified generation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * refactor: move plugin OpenAPI specs to api/openapi/src/plugins/ Co-locate plugin specs with other OpenAPI sources under api/openapi/src/ instead of catalog/plugins/*/api/openapi/. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * refactor: consolidate plugin OpenAPI specs into single files Flatten per-plugin directory structure (openapi.yaml + components.yaml) into single files (model.yaml, mcp.yaml) under api/openapi/src/plugins/. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: update .gitattributes and clean target for per-plugin generation Regenerate .gitattributes to reflect api.go → api_model.go/api_mcp.go rename. Fix clean-internal-server-openapi to also delete per-plugin output files so both plugins regenerate after a clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: remove generator boilerplate after per-plugin generation Clean up README.md, api/openapi.yaml, and .openapi-generator-ignore after each plugin generation run to prevent untracked files in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> --------- Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8ff4a8e commit 86eb885

17 files changed

Lines changed: 2082 additions & 1713 deletions

File tree

.gitattributes

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ catalog/clients/python/src/catalog_openapi/models/model_preview_result.py lingui
6868
catalog/clients/python/src/catalog_openapi/models/order_by_field.py linguist-generated=true
6969
catalog/clients/python/src/catalog_openapi/models/sort_order.py linguist-generated=true
7070
catalog/clients/python/src/catalog_openapi/rest.py linguist-generated=true
71-
catalog/internal/server/openapi/api.go linguist-generated=true
71+
catalog/internal/server/openapi/api_mcp.go linguist-generated=true
7272
catalog/internal/server/openapi/api_mcp_catalog_service.go linguist-generated=true
73+
catalog/internal/server/openapi/api_model.go linguist-generated=true
7374
catalog/internal/server/openapi/api_model_catalog_service.go linguist-generated=true
7475
catalog/internal/server/openapi/error.go linguist-generated=true
7576
catalog/internal/server/openapi/helpers.go linguist-generated=true

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ gen/converter: internal/converter/generated/converter.go
6666
api/openapi/model-registry.yaml: api/openapi/src/model-registry.yaml api/openapi/src/lib/*.yaml bin/yq
6767
scripts/merge_openapi.sh model-registry.yaml
6868

69-
api/openapi/catalog.yaml: api/openapi/src/catalog.yaml api/openapi/src/lib/*.yaml bin/yq
70-
scripts/merge_openapi.sh catalog.yaml
69+
api/openapi/catalog.yaml: api/openapi/src/catalog.yaml api/openapi/src/lib/*.yaml $(wildcard api/openapi/src/plugins/*.yaml) bin/yq
70+
scripts/merge_catalog_specs.sh catalog.yaml
7171

7272
# validate the openapi schema
7373
.PHONY: openapi/validate
7474
openapi/validate: bin/openapi-generator-cli bin/yq
7575
@scripts/merge_openapi.sh --check model-registry.yaml || (echo "api/openapi/model-registry.yaml is incorrectly formatted. Run 'make api/openapi/model-registry.yaml' to fix it."; exit 1)
76-
@scripts/merge_openapi.sh --check catalog.yaml || (echo "$< is incorrectly formatted. Run 'make api/openapi/catalog.yaml' to fix it."; exit 1)
76+
@scripts/merge_catalog_specs.sh --check catalog.yaml || (echo "api/openapi/catalog.yaml is incorrectly formatted. Run 'make api/openapi/catalog.yaml' to fix it."; exit 1)
7777
$(OPENAPI_GENERATOR) validate -i api/openapi/model-registry.yaml
7878
$(OPENAPI_GENERATOR) validate -i api/openapi/catalog.yaml
7979

api/openapi/catalog.yaml

Lines changed: 103 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,6 +2117,79 @@ components:
21172117
$ref: "#/components/schemas/Error"
21182118
description: Unprocessable Entity error
21192119
parameters:
2120+
filterQuery:
2121+
examples:
2122+
filterQuery:
2123+
value: "name='my-model' AND state='LIVE'"
2124+
name: filterQuery
2125+
description: |
2126+
A SQL-like query string to filter the list of entities. The query supports rich filtering capabilities with automatic type inference.
2127+
2128+
**Supported Operators:**
2129+
- Comparison: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=`
2130+
- Pattern matching: `LIKE`, `ILIKE` (case-insensitive)
2131+
- Set membership: `IN`
2132+
- Logical: `AND`, `OR`
2133+
- Grouping: `()` for complex expressions
2134+
2135+
**Data Types:**
2136+
- Strings: `"value"` or `'value'`
2137+
- Numbers: `42`, `3.14`, `1e-5`
2138+
- Booleans: `true`, `false` (case-insensitive)
2139+
2140+
**Property Access:**
2141+
- Standard properties: `name`, `id`, `state`, `createTimeSinceEpoch`
2142+
- Custom properties: Any user-defined property name
2143+
- Escaped properties: Use backticks for special characters: `` `custom-property` ``
2144+
- Type-specific access: `property.string_value`, `property.double_value`, `property.int_value`, `property.bool_value`
2145+
2146+
**Examples:**
2147+
- Basic: `name = "my-model"`
2148+
- Comparison: `accuracy > 0.95`
2149+
- Pattern: `name LIKE "%tensorflow%"`
2150+
- Complex: `(name = "model-a" OR name = "model-b") AND state = "LIVE"`
2151+
- Custom property: `framework.string_value = "pytorch"`
2152+
- Escaped property: `` `mlflow.source.type` = "notebook" ``
2153+
schema:
2154+
type: string
2155+
pattern: "^[\\x20-\\x7E]*$"
2156+
in: query
2157+
required: false
2158+
orderBy:
2159+
style: form
2160+
explode: true
2161+
examples:
2162+
orderBy:
2163+
value: ID
2164+
name: orderBy
2165+
description: Specifies the order by criteria for listing entities.
2166+
schema:
2167+
$ref: "#/components/schemas/OrderByField"
2168+
in: query
2169+
required: false
2170+
labelOrderBy:
2171+
style: form
2172+
explode: true
2173+
examples:
2174+
labelOrderBy:
2175+
value: name
2176+
name: orderBy
2177+
description: |
2178+
Specifies the key to order catalog labels by. You can provide any string key
2179+
that may exist in the label maps. Labels that contain the specified key will
2180+
be sorted by that key's value. Labels that don't contain the key will maintain
2181+
their original order and appear after labels that do contain the key.
2182+
schema:
2183+
type: string
2184+
in: query
2185+
required: false
2186+
assetType:
2187+
name: assetType
2188+
description: Filter by asset type.
2189+
schema:
2190+
$ref: "#/components/schemas/CatalogAssetType"
2191+
in: query
2192+
required: false
21202193
mcpServerFilterQuery:
21212194
examples:
21222195
mcpServerFilterQuery:
@@ -2167,42 +2240,39 @@ components:
21672240
pattern: "^[\\x20-\\x7E]*$"
21682241
in: query
21692242
required: false
2170-
filterQuery:
2243+
namedQuery:
21712244
examples:
2172-
filterQuery:
2173-
value: "name='my-model' AND state='LIVE'"
2174-
name: filterQuery
2175-
description: |
2176-
A SQL-like query string to filter the list of entities. The query supports rich filtering capabilities with automatic type inference.
2177-
2178-
**Supported Operators:**
2179-
- Comparison: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=`
2180-
- Pattern matching: `LIKE`, `ILIKE` (case-insensitive)
2181-
- Set membership: `IN`
2182-
- Logical: `AND`, `OR`
2183-
- Grouping: `()` for complex expressions
2184-
2185-
**Data Types:**
2186-
- Strings: `"value"` or `'value'`
2187-
- Numbers: `42`, `3.14`, `1e-5`
2188-
- Booleans: `true`, `false` (case-insensitive)
2189-
2190-
**Property Access:**
2191-
- Standard properties: `name`, `id`, `state`, `createTimeSinceEpoch`
2192-
- Custom properties: Any user-defined property name
2193-
- Escaped properties: Use backticks for special characters: `` `custom-property` ``
2194-
- Type-specific access: `property.string_value`, `property.double_value`, `property.int_value`, `property.bool_value`
2195-
2196-
**Examples:**
2197-
- Basic: `name = "my-model"`
2198-
- Comparison: `accuracy > 0.95`
2199-
- Pattern: `name LIKE "%tensorflow%"`
2200-
- Complex: `(name = "model-a" OR name = "model-b") AND state = "LIVE"`
2201-
- Custom property: `framework.string_value = "pytorch"`
2202-
- Escaped property: `` `mlflow.source.type` = "notebook" ``
2245+
namedQuery:
2246+
value: secure_servers
2247+
name: namedQuery
2248+
description: Predefined filter template name to apply when listing MCP servers.
22032249
schema:
22042250
type: string
2205-
pattern: "^[\\x20-\\x7E]*$"
2251+
in: query
2252+
required: false
2253+
includeTools:
2254+
examples:
2255+
includeTools:
2256+
value: true
2257+
name: includeTools
2258+
description: Whether to include the tools array in each MCP server result.
2259+
schema:
2260+
type: boolean
2261+
default: false
2262+
in: query
2263+
required: false
2264+
toolLimit:
2265+
examples:
2266+
toolLimit:
2267+
value: 10
2268+
name: toolLimit
2269+
description: Maximum number of tools to include when includeTools is true.
2270+
schema:
2271+
type: integer
2272+
format: int32
2273+
default: 10
2274+
minimum: 0
2275+
maximum: 100
22062276
in: query
22072277
required: false
22082278
artifactFilterQuery:
@@ -2243,18 +2313,6 @@ components:
22432313
pattern: "^[\\x20-\\x7E]*$"
22442314
in: query
22452315
required: false
2246-
orderBy:
2247-
style: form
2248-
explode: true
2249-
examples:
2250-
orderBy:
2251-
value: ID
2252-
name: orderBy
2253-
description: Specifies the order by criteria for listing entities.
2254-
schema:
2255-
$ref: "#/components/schemas/OrderByField"
2256-
in: query
2257-
required: false
22582316
artifactOrderBy:
22592317
style: form
22602318
explode: true
@@ -2307,64 +2365,6 @@ components:
23072365
type: string
23082366
in: query
23092367
required: false
2310-
labelOrderBy:
2311-
style: form
2312-
explode: true
2313-
examples:
2314-
labelOrderBy:
2315-
value: name
2316-
name: orderBy
2317-
description: |
2318-
Specifies the key to order catalog labels by. You can provide any string key
2319-
that may exist in the label maps. Labels that contain the specified key will
2320-
be sorted by that key's value. Labels that don't contain the key will maintain
2321-
their original order and appear after labels that do contain the key.
2322-
schema:
2323-
type: string
2324-
in: query
2325-
required: false
2326-
namedQuery:
2327-
examples:
2328-
namedQuery:
2329-
value: secure_servers
2330-
name: namedQuery
2331-
description: Predefined filter template name to apply when listing MCP servers.
2332-
schema:
2333-
type: string
2334-
in: query
2335-
required: false
2336-
includeTools:
2337-
examples:
2338-
includeTools:
2339-
value: true
2340-
name: includeTools
2341-
description: Whether to include the tools array in each MCP server result.
2342-
schema:
2343-
type: boolean
2344-
default: false
2345-
in: query
2346-
required: false
2347-
toolLimit:
2348-
examples:
2349-
toolLimit:
2350-
value: 10
2351-
name: toolLimit
2352-
description: Maximum number of tools to include when includeTools is true.
2353-
schema:
2354-
type: integer
2355-
format: int32
2356-
default: 10
2357-
minimum: 0
2358-
maximum: 100
2359-
in: query
2360-
required: false
2361-
assetType:
2362-
name: assetType
2363-
description: Filter by asset type.
2364-
schema:
2365-
$ref: "#/components/schemas/CatalogAssetType"
2366-
in: query
2367-
required: false
23682368
artifactType:
23692369
style: form
23702370
explode: true

0 commit comments

Comments
 (0)