From d2fc8517b055aa559d8095f308f08fc124b251e3 Mon Sep 17 00:00:00 2001 From: David Ragot <35502263+Dav-14@users.noreply.github.com> Date: Fri, 6 Nov 2026 12:15:34 +0100 Subject: [PATCH 1/3] fix(openapi): keep composition validation component-agnostic --- Justfile | 11 ----------- README.md | 5 +++++ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Justfile b/Justfile index 66257e345c..0a2a893b92 100644 --- a/Justfile +++ b/Justfile @@ -64,21 +64,10 @@ validate-openapi: jq -e --argjson base_info "$(yq -o=json -I=0 '.info' releases/base.yaml)" '(.info | .version = $base_info.version) == $base_info' releases/build/generate.json >/dev/null # Every local discriminator mapping must resolve to an existing schema. jq -e '.components.schemas as $schemas | [ $schemas | .. | objects | select(.discriminator.mapping? != null) | .discriminator.mapping[] | select(type == "string" and startswith("#/components/schemas/")) | sub("^#/components/schemas/"; "") | select($schemas[.] == null) ] | length == 0' releases/build/generate.json >/dev/null - # Ledger query-template helpers rely on these resource constants. - jq -e '[.components.schemas.ledger_V2QueryParams.oneOf[].properties.resource | select(.const == null or .enum != null)] | length == 0' releases/build/generate.json >/dev/null # Preserve typed default errors instead of shadowing them with generic range matchers. test "$(jq -c '.paths["x-speakeasy-errors"].statusCodes' releases/build/generate.json)" = '["default"]' # Every operation tag must be declared globally. jq -e '[.tags[].name] as $tags | [.paths[] | to_entries[] | select(.key | IN("get", "post", "put", "patch", "delete", "options", "head", "trace")) | .value.tags[]? | select(. as $tag | $tags | index($tag) == null)] | length == 0' releases/build/generate.json >/dev/null - # Speakeasy keeps different operations on the shared Ledger info path under - # namespace fragments; generated SDK requests omit those fragments. - jq -e '[.paths | keys[] | select(contains("#") and . != "/api/ledger/_info#ledger" and . != "/api/ledger/_info#ledgerV3")] | length == 0' releases/build/generate.json >/dev/null - # Preserve both info contracts: V1's data envelope and V3's build metadata. - jq -e '.paths["/api/ledger/_info#ledger"].get as $v1 | .paths["/api/ledger/_info#ledgerV3"].get as $v3 | $v1.operationId == "getInfo" and $v1.tags == ["ledger.v1"] and $v1.responses["200"].content["application/json"].schema["$ref"] == "#/components/schemas/ledger_ConfigInfoResponse" and $v3.operationId == "v3Info" and $v3.tags == ["ledger.v3"] and $v3.responses["200"].content["application/json"].schema["$ref"] == "#/components/schemas/ledgerV3_ServerInfo"' releases/build/generate.json >/dev/null - # Both supported Ledger API generations must remain in the composed spec. - jq -e '.paths["/api/ledger/v2"] != null and .paths["/api/ledger/v3/"] != null' releases/build/generate.json >/dev/null - # Composition cleanups must remain applied when component specs change. - jq -e '.components.schemas.auth_Scope == null and .components.schemas.auth_ScopeOptions == null' releases/build/generate.json >/dev/null jq -e '[.. | objects | select(.enum? | type == "array") | .enum | select(length != (unique | length))] | length == 0' releases/build/generate.json >/dev/null # Generate event schemas diff --git a/README.md b/README.md index c344d5d47a..827ac529b3 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,11 @@ The Formance Stack is built on open-source, battle tested technologies including ## OpenAPI composition `just build-openapi` composes the component contracts and validates the result. +Validation checks canonical API metadata, discriminator references, shared SDK +error configuration, declared operation tags, and duplicate enum values. +Component-specific paths and schema shapes are owned by the component contracts; +Stack does not assert their internal structure. + Ledger V1 and V3 both expose `GET /api/ledger/_info` with different response models. Both operations remain in the composed document as `/api/ledger/_info#ledger` and `/api/ledger/_info#ledgerV3`. These are Speakeasy From 072263e361c5a1d8bde3b088b61be9e6d44f5c67 Mon Sep 17 00:00:00 2001 From: David Ragot <35502263+Dav-14@users.noreply.github.com> Date: Fri, 6 Nov 2026 12:19:31 +0100 Subject: [PATCH 2/3] fix(openapi): limit validation removal to recent Ledger info checks --- Justfile | 6 ++++++ README.md | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Justfile b/Justfile index 0a2a893b92..4b15adfd28 100644 --- a/Justfile +++ b/Justfile @@ -64,10 +64,16 @@ validate-openapi: jq -e --argjson base_info "$(yq -o=json -I=0 '.info' releases/base.yaml)" '(.info | .version = $base_info.version) == $base_info' releases/build/generate.json >/dev/null # Every local discriminator mapping must resolve to an existing schema. jq -e '.components.schemas as $schemas | [ $schemas | .. | objects | select(.discriminator.mapping? != null) | .discriminator.mapping[] | select(type == "string" and startswith("#/components/schemas/")) | sub("^#/components/schemas/"; "") | select($schemas[.] == null) ] | length == 0' releases/build/generate.json >/dev/null + # Ledger query-template helpers rely on these resource constants. + jq -e '[.components.schemas.ledger_V2QueryParams.oneOf[].properties.resource | select(.const == null or .enum != null)] | length == 0' releases/build/generate.json >/dev/null # Preserve typed default errors instead of shadowing them with generic range matchers. test "$(jq -c '.paths["x-speakeasy-errors"].statusCodes' releases/build/generate.json)" = '["default"]' # Every operation tag must be declared globally. jq -e '[.tags[].name] as $tags | [.paths[] | to_entries[] | select(.key | IN("get", "post", "put", "patch", "delete", "options", "head", "trace")) | .value.tags[]? | select(. as $tag | $tags | index($tag) == null)] | length == 0' releases/build/generate.json >/dev/null + # Both supported Ledger API generations must remain in the composed spec. + jq -e '.paths["/api/ledger/v2"] != null and .paths["/api/ledger/v3/"] != null' releases/build/generate.json >/dev/null + # Composition cleanups must remain applied when component specs change. + jq -e '.components.schemas.auth_Scope == null and .components.schemas.auth_ScopeOptions == null' releases/build/generate.json >/dev/null jq -e '[.. | objects | select(.enum? | type == "array") | .enum | select(length != (unique | length))] | length == 0' releases/build/generate.json >/dev/null # Generate event schemas diff --git a/README.md b/README.md index 827ac529b3..c344d5d47a 100644 --- a/README.md +++ b/README.md @@ -58,11 +58,6 @@ The Formance Stack is built on open-source, battle tested technologies including ## OpenAPI composition `just build-openapi` composes the component contracts and validates the result. -Validation checks canonical API metadata, discriminator references, shared SDK -error configuration, declared operation tags, and duplicate enum values. -Component-specific paths and schema shapes are owned by the component contracts; -Stack does not assert their internal structure. - Ledger V1 and V3 both expose `GET /api/ledger/_info` with different response models. Both operations remain in the composed document as `/api/ledger/_info#ledger` and `/api/ledger/_info#ledgerV3`. These are Speakeasy From e5a56d60cbca2eb7b3185d11e5abbb3b16793204 Mon Sep 17 00:00:00 2001 From: David Ragot <35502263+Dav-14@users.noreply.github.com> Date: Fri, 6 Nov 2026 12:43:04 +0100 Subject: [PATCH 3/3] fix(openapi): drop obsolete Ledger query params assertion --- Justfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Justfile b/Justfile index 4b15adfd28..dd037c6dc4 100644 --- a/Justfile +++ b/Justfile @@ -64,8 +64,6 @@ validate-openapi: jq -e --argjson base_info "$(yq -o=json -I=0 '.info' releases/base.yaml)" '(.info | .version = $base_info.version) == $base_info' releases/build/generate.json >/dev/null # Every local discriminator mapping must resolve to an existing schema. jq -e '.components.schemas as $schemas | [ $schemas | .. | objects | select(.discriminator.mapping? != null) | .discriminator.mapping[] | select(type == "string" and startswith("#/components/schemas/")) | sub("^#/components/schemas/"; "") | select($schemas[.] == null) ] | length == 0' releases/build/generate.json >/dev/null - # Ledger query-template helpers rely on these resource constants. - jq -e '[.components.schemas.ledger_V2QueryParams.oneOf[].properties.resource | select(.const == null or .enum != null)] | length == 0' releases/build/generate.json >/dev/null # Preserve typed default errors instead of shadowing them with generic range matchers. test "$(jq -c '.paths["x-speakeasy-errors"].statusCodes' releases/build/generate.json)" = '["default"]' # Every operation tag must be declared globally.