Improve Canton openApi Docs generating directly from annotated protos - #2
Conversation
b9356fb to
b24f873
Compare
|
Internal review (2026-07-10, pre-merge into fork
Metadata-only by construction — matches the 2026-07-03 audit recorded in SDK ADR 0012. Merging into this fork's |
…e options Demonstrates Option 4 from the C# SDK proposal appendix: annotate Canton's .proto files with google.api.http so the JSON HTTP surface can be derived from the protos directly, bypassing tapir's translation layer. Scope (one service end-to-end): - community/ledger-api-proto/.../v2/command_service.proto: imports google/api/annotations.proto and adds an option (google.api.http) block to each of the three RPCs (SubmitAndWait, SubmitAndWaitForTransaction, SubmitAndWaitForReassignment), matching the existing tapir routes. - All v2 .proto files (22) plus value.proto: add option go_package next to the existing csharp_namespace/java_package options. Required by every off-the-shelf OpenAPI generator in the ecosystem. Harmless to existing C#/Java/Scala codegen, which does not read this option. Verified with `buf build`: clean compile. Verified end-to-end OpenAPI generation with buf.build/grpc-ecosystem/openapiv2:v2.28.0: the output fixes all four type-system defects catalogued in the C# SDK proposal (numbered duplicate schemas, single-key oneOf wrappers, inline duplicated enums, untyped DAML payload fields). See peacefulstudio/documentation/dev-funding/openApi/upstream-fork-experiment.md for the full write-up.
Builds on the CommandService experiment by annotating every other v2 RPC
the JSON API exposes over HTTP, plus adding go_package to the admin/,
testing/, and interactive/ sub-packages so off-the-shelf OpenAPI
generators can resolve the full proto closure.
Annotated services (route source: community/ledger/ledger-json-api Js*Service.scala):
v2/
- CommandSubmissionService: Submit, SubmitReassignment
- CommandCompletionService: CompletionStream
- ContractService: GetContract
- EventQueryService: GetEventsByContractId
- PackageService: ListPackages, GetPackage,
GetPackageStatus, ListVettedPackages
- StateService: GetActiveContracts, GetActiveContractsPage,
GetConnectedSynchronizers, GetLedgerEnd,
GetLatestPrunedOffsets
- UpdateService: GetUpdates, GetUpdateByOffset,
GetUpdateById, GetUpdatesPage
- VersionService: GetLedgerApiVersion
v2/admin/
- IdentityProviderConfigService: 5 RPCs
- PackageManagementService: UploadDarFile, ValidateDarFile,
UpdateVettedPackages
- PartyManagementService: 7 HTTP-exposed RPCs
- UserManagementService: 9 RPCs
v2/interactive/
- InteractiveSubmissionService: 6 RPCs
Each annotation matches the existing tapir route (method + path + body
mapping) so the proto-derived OpenAPI is comparable to the current
tapir-emitted document.
Scope notes:
- Server-streaming RPCs (CompletionStream, GetActiveContracts,
GetUpdates) get only the canonical POST/list mapping; the WebSocket
GET form is a JSON-API-only convenience that has no representation in
google.api.http and remains in the AsyncAPI doc.
- Legacy JSON-only paths (/v2/updates/flats|trees|transaction-*,
/v2/authenticated-user, /v2/definitions/*) are not annotated — they
either lack a 1:1 proto RPC or are non-gRPC custom endpoints.
- Non-HTTP-exposed RPCs (TimeService, CommandInspectionService,
ParticipantPruningService, PackageManagementService.ListKnownPackages,
PartyManagementService.UpdatePartyIdentityProviderId) are deliberately
left unannotated.
Verified locally with `buf build community/ledger-api-proto/src/main/protobuf`:
clean compile.
The JSON API exposes this as GET with a JSON body containing a required
EventFormat message — non-standard REST, and rejected by
grpc-ecosystem/openapiv2 ("must not set request body when http method
is GET"). Switching to POST is the canonical mapping for a request
with a non-trivial body, and is the single intentional deviation from
the existing tapir route for the proto-derived OpenAPI.
Mirrors the experiment-evidence pattern from the single-service experiment, scaled to the full HTTP API: - openapi-3.0/openapi.yaml — OpenAPI 3.0.3 YAML generated via buf.build/community/google-gnostic-openapi:v0.7.0; same major version as the existing community/ledger/ledger-json-api/.../openapi.yaml, so the two can be diffed directly. - swagger-2.0/canton-ledger-api.swagger.yaml — Swagger 2.0 YAML via buf.build/grpc-ecosystem/openapiv2:v2.28.0; same plugin as the upstream CommandService experiment, merged across every annotated service. - coverage-comparison/ — sorted path inventories and the diff between the JSON API spec and the proto-derived spec, after normalizing path-parameter names (proto field names ↔ tapir placeholder names). 9 paths-only-in-JSON-API (all deliberate skips: 1 custom JSON-only endpoint + 8 deprecated legacy aliases), 0 paths-only-in-proto. - reproduce.sh — one-shot regen of every artefact in this directory. Internal to the Peaceful Studio fork. To be deleted before any upstream PR to digital-asset/canton.
aa5ec09 to
472b36a
Compare
|
Superseded by #4. This PR was squash-merged by accident on 2026-07-26. Auto-merge had been armed here since 2026-07-10, blocked only by conflicts with The merge was not intended.
The same work is now open as #4 against the pristine base, with auto-merge deliberately off. Review there. This PR cannot be reopened (GitHub disallows reopening a merged PR), so it stays closed as a record of what happened. |
Before : SwaggerHub with current openApi Docs
After : SwaggerHub with proto generated openApi Docs
Extends #1 from a single service to every HTTP-exposed RPC of the JSON Ledger API.
Builds on the same design and rationale — see #1 for the why.
What's done
community/ledger-api-proto:go_packageoption added to admin/, testing/, interactive/ protos that experiment: google.api.http annotations on CommandService (Peaceful Studio internal) #1 didn't touch.buf buildpasses oncommunity/ledger-api-proto/src/main/protobuf.http-annotations-evidence/:openapi-3.0/openapi.yaml(3.0.3, comparable to the existing JSON-API yaml) — SwaggerHub: https://app.swaggerhub.com/apis/peacefulstudio/canton-ledger-api-proto-derived/v2#/swagger-2.0/canton-ledger-api.swagger.yaml(same plugin as experiment: google.api.http annotations on CommandService (Peaceful Studio internal) #1).coverage-comparison/— diff vs the existing yaml shows 9 paths only in JSON-API (all deliberate skips) and 0 paths only in proto.reproduce.shregenerates every artefact from the protos.Intentional deviations from tapir
StateService.GetActiveContractsPageis POST, not GET. The tapir route uses GET-with-body, whichgrpc-ecosystem/openapiv2rejects and which OpenAPI generally disallows. The request carries a requiredEventFormatmessage that doesn't flatten to query params.CompletionStream,GetActiveContracts,GetUpdates) get the POST list mapping only. The WebSocket GET form is JSON-API-only and stays in the AsyncAPI doc.Deliberately not annotated
/v2/commands/submit-and-wait-for-transaction-tree,/v2/package-vetting,/v2/updates/{flats,trees,transaction-*})./v2/authenticated-user— custom JSON-only, no gRPC backing.TimeService,CommandInspectionService,ParticipantPruningService,PackageManagementService.ListKnownPackages,PartyManagementService.UpdatePartyIdentityProviderId).Left to do (not in this PR)
GetActiveContractsPageto POST so the JSON-API and the proto-derived spec agree on that one route.additional_bindingsfor the 3.4 → 3.5 transition window, or treat their removal from the spec as a feature of this change.http-annotations-evidence/and split into the minimal commits before any PR to digital-asset/canton.Test plan
buf build community/ledger-api-proto/src/main/protobufis clean.bash http-annotations-evidence/reproduce.shreproduces the committed yamls byte-for-byte.go_packageis inert for them — proven in experiment: google.api.http annotations on CommandService (Peaceful Studio internal) #1).Folded from #1 (2026-07-10)
#1 is closed as superseded; this PR is now self-contained:
experiment-evidence/is carried onto this branch: thego-package-harmlessness/proof (before/after descriptor diffs showingoption go_packageis inert for the existing Scala/Java/C# codegen) and the original CommandService openapi POC. The full original write-up and context stay readable on experiment: google.api.http annotations on CommandService (Peaceful Studio internal) #1.mainonly.GetUpdatesPage/GetActiveContractsPageare 3.5-era and wait for the version bump).