Repository navigation
feat(openapi): add profile-driven OpenAPI 3.1 service and type binding (generate-openapi) - #89
Conversation
ba67cfb to
14edfcd
Compare
14edfcd to
0cc5894
Compare
0cf816a to
86c1bf1
Compare
0cc5894 to
4387d0f
Compare
86c1bf1 to
e1c906e
Compare
82faf0a to
c540328
Compare
28ba505 to
ce122a4
Compare
0405926 to
8f9d2f3
Compare
65b0a52 to
fdd65fb
Compare
8f9d2f3 to
6dc0aaf
Compare
fdd65fb to
fc967d0
Compare
6dc0aaf to
3f2732d
Compare
|
Reviewed this as a downstream consumer: our conformance suite drives The binding itself does what the body says. On the ucp tree at 1.
info: json!({ "title": "UCP REST Service", "version": "1.0.0" }),and then, for the first selected service at if let Some(info) = root_obj.get("info") {
merged.info = info.clone();
}Every UCP REST service template has an
The four without it are every REST service template in the tree; the one with it is the tokenization handler, which is not a service binding. So this fires for any REST profile rather than on an edge case. Observed, with a minimal shopping profile pointing at the local tree: (the validator also dumps the The tool exits 0 with empty stderr. Why the tests do not catch it: Nor has any job run them. Happy to send the |
5b4a2da to
40c88a0
Compare
700efcc to
273ae29
Compare
40c88a0 to
4c8b88e
Compare
f631dab to
652d786
Compare
850b9fd to
9557e30
Compare
652d786 to
706729c
Compare
9557e30 to
33a8af6
Compare
706729c to
81166bd
Compare
33a8af6 to
0572660
Compare
81166bd to
ecb38b9
Compare
0572660 to
7805e50
Compare
| if ep.is_empty() || !seen.insert(ep.to_string()) { | ||
| continue; | ||
| } | ||
| endpoints.push(json!({ "url": ep })); |
There was a problem hiding this comment.
Given this is a vector, it will collected all the selected service's endpoint into one array. So if a developer chooses to support multiple services in their profile, we would end up with multiple server URLs but never specify which sub-path is relevant for which server (this ends up making an assumption that all paths are applicable for all server endpoints) - this would become a big problem for any REST transport bindings.
It might be better to return a map of server endpoint to applicable paths instead.
There was a problem hiding this comment.
Great catch! Addressed in 011f26c:
- Each selected REST service template is now bound and pruned independently before merging (so inactive services do not leak endpoints/metadata, and colliding active routes across distinct services return
CodegenError::ConflictingServicePath). - When retained routes span distinct service endpoints, root
serversis omitted and each retainedPath Item Objectreceives its own OAS 3.1serversarray (paths[path].servers); when all retained routes share an identical server list (single service,--servicefilter,--server-urloverride, or shared gateway URL), rootserversis emitted cleanly with no per-path repetition.
ecb38b9 to
3563d15
Compare
…g (generate-openapi)
…ervers - Bind and prune each selected REST service template independently before merging so inactive services do not leak endpoints, info, or parameters and cross-service path collisions fail with ConflictingServicePath (exit code 2) - Emit OAS 3.1 Path Item Object.servers (omitting root servers) when retained routes span distinct service endpoints while keeping uniform endpoints at root servers - Guard against whitespace-only version strings in template info objects and add CI-unconditional multi-service fixture tests
3563d15 to
011f26c
Compare
|
Thanks @jingyli and @vishkaty for the thorough reviews!
|
|
Confirmed on main at 2ce04cc from a fresh clone: the control profile emits |
Summary
Implements Task 11 (PR 5) of the two-layer
ucp-schemacode-generation pipeline: Layer 2 profile-driven OpenAPI 3.1.0 service and type binding (ucp-schema generate-openapi/src/openapi/mod.rs), stacked on #88.Changes
src/openapi/mod.rs,src/codegen/profile.rs):GenerateOpenApiOptions(profile,service,server_url),rewrite_defs_to_component_schemas, andgenerate_openapi().parse_profile_source(), filters"transport": "rest"service bindings by optional--service, and returnsCodegenError::NoRestServiceBinding(exit code 2) on MCP/embedded-only profiles orCodegenError::MissingServiceSchema(exit code 2) when"schema"is absent.services/{shopping,lodging,common}/rest.openapi.json), setting"jsonSchemaDialect": "https://spec.openapis.org/oas/3.1/dialect/base"and populating"servers"from--server-urlor the profile's deduplicated REST"endpoint"URLs.compile_types()and prunes inactive routes inpathsandwebhooks(removing"webhooks"when empty).components.schemasand binds operationrequestBodyandresponsesdirectly to canonical PascalCase#/components/schemas/<Name>definitions (<Cap>{Create,Update,Complete}Request, container operation request/response schemas,Order,UcpBase, and inlinedoneOf: [<Primary>, ErrorResponse]on error-wrapped responses).#/$defs/<Name>to#/components/schemas/<Name>and prunescomponents.schemas,components.parameters, andcomponents.headersto the transitive reachability closure of the retained routes and webhooks (with zero unused ambient schemas or parameters and zero"discriminator"keywords).src/lib.rs,src/bin/ucp-schema.rs):pub mod openapi;andpub use openapi::{generate_openapi, rewrite_defs_to_component_schemas, GenerateOpenApiOptions};insrc/lib.rs.ucp-schema generate-openapi(--profile/-p,--service,--server-url,--output/-o,--pretty) insrc/bin/ucp-schema.rs.tests/generate_openapi_test.rs):checkout+discount+fulfillment,order+webhooks.orderEvent,catalog.search,catalog.lookup), common (location.search,location.lookup,ask), lodging (booking), multi-service merging &--servicefiltering,--server-urloverride, error exit codes, and OpenAPI 3.1 structural invariants.Stacked PR Series Roadmap
$refrewriting (src/codegen/normalizer.rs)$refalignment (src/codegen/normalizer.rs)generate_types()library API (src/codegen/{mod,reachability,hoist,compose}.rs)generate-typessubcommand (src/bin/ucp-schema.rs,tests/generate_types_test.rs).well-known/ucp--profilescoping (src/codegen/profile.rs)src/codegen/normalizer.rs,src/codegen/reify.rs)<Union>Base) (src/codegen/reify.rs)src/codegen/reify.rs)generate_models.sh,postprocess_models.py,src/ucp_sdk/models/)generate_models.sh,scripts/generate-zod-from-types.mjs,src/spec_generated.ts)ucp-schema generate-openapi,src/openapi/mod.rs)Test Plan
cargo fmt --checkcargo clippy --all-targets -- -D warningscargo test --all-targets