Skip to content

refactor: single source of truth for resource-doc registry (closes BG v1.3 alias gap) - #2902

Open
hongwei1 wants to merge 7 commits into
OpenBankProject:developfrom
hongwei1:feature/bg-v13-alias-resource-doc-registry
Open

refactor: single source of truth for resource-doc registry (closes BG v1.3 alias gap)#2902
hongwei1 wants to merge 7 commits into
OpenBankProject:developfrom
hongwei1:feature/bg-v13-alias-resource-doc-registry

Conversation

@hongwei1

@hongwei1 hongwei1 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Summary

A bug report proposed a ResourceDocRegistry refactor for a production bug: adding BGv2-getAccountDetails to an API Collection failed with an invalid-operation-id error, because OBP-API kept two hand-maintained resource-doc registries (the per-version dispatcher used by /resource-docs/{VERSION}/... and API Explorer, and the union in APIUtil.allStaticResourceDocs/getAllResourceDocs used wherever an operation id must be resolved) and they had drifted.

Two corrections to that report, verified directly against develop at the time this branch was cut:

  • The error code cited (OBP-30051) is wrong — the actual failure is OBP-40048: Invalid operation_id.
  • The report's "interim fix, uncommitted in my working tree" was already merged (commits a1231e3f5 / 60b9f75d1) before this branch was cut. That merged fix closed the BGv2 gap and the v6-vs-v7 aggregation gap, and already added ResourceDocRegistryParityTest.

This PR verifies that merged fix end-to-end, closes the drift instance it left behind (the Berlin Group v1.3 alias), and — following up on the repo owner's answers to the three open questions the original report raised — implements the proposed refactor: a single ResourceDocRegistry object that both the dispatcher and the global union now derive from, so this class of drift becomes structurally impossible rather than something that has to be remembered by hand each time a standard is added.

Changes

  1. fix: add OBP_BERLIN_GROUP_1_3_Alias.allResourceDocs to APIUtil.allStaticResourceDocs (superseded by commit 4, kept as its own step for bisectability). Reproduced the failure first (OBP-40048 on a valid alias operation id against an instance with berlin_group_v1_3_alias_path set), then confirmed the fix resolves it.
  2. test: extend ResourceDocRegistryParityTest to cover the BG v1.3 alias surface.
  3. refactor: remove ResourceDocsAPIMethods's activeResourceDocs match block — all ~19 arms were confirmed pure identity functions, a leftover from the pre-http4s Lift route-filter era.
  4. refactor: introduce code.api.util.ResourceDocRegistry as the single source of truth for "which resource docs does version X serve". Http4sBGv2 becomes a ScannedApis registrant (fully convention-driven, like the other Berlin Group / UK Open Banking standards — no more hand-maintained entry or special case in ApiVersionUtils.valueOf). The global union is now deduped by operationId. ResourceDocRegistryParityTest is rewritten to iterate the registry itself rather than a hand-typed list of standards, so a newly added standard is covered by construction.
  5. test: add an HTTP-level regression test pinning BGv2-getAccountDetails as a resolvable api-collection-endpoint operation id (the exact request from the sandbox bug report) — previously only covered by a unit-level membership check.
  6. test: enable the Berlin Group v1.3 alias by default in the test environment (berlin_group_v1_3_alias_path=0.6/v1 in test.default.props and both CI workflows' generated copy of it), so its operation ids get the same HTTP-level regression coverage as commit 5, not just a unit-level check. This couldn't be done by toggling the prop per-test at runtime — the alias's ScannedApiVersion identity is captured once by a process-wide classpath scan (ScannedApis.versionMapScannedApis, a lazy val shared across the whole JVM/shard) that gets forced by the first unrelated request that falls through the route chain, almost always before any test-specific prop override could run.

Test plan

  • POST /obp/v4.0.0/my/api-collections/{name}/api-collection-endpoints with operation_id=BGv2-getAccountDetails201 (re-confirms the already-merged fix, both before and after the registry refactor)
  • Same flow exercised through API-Explorer-II's real favourite-star UI (Content.vuecreateMyAPICollectionEndpoint), not just curl
  • Reproduced OBP-40048 on a BG v1.3 alias operation id against an instance with berlin_group_v1_3_alias_path set, before the fix; 201 after
  • Same alias check re-run live after the registry refactor landed — still 201
  • ApiCollectionEndpointTest now exercises both BGv2-getAccountDetails and the alias's BGv1-getPaymentInitiationStatus as real HTTP requests, alongside existing OBPv6.0.0/UK Open Banking/Berlin Group v1.3 canonical coverage
  • ResourceDocRegistryParityTest pins both operation ids and is registry-driven end to end
  • ResourceDocsTest / SwaggerDocsTest / V7ResourceDocsAggregationTest / Http4sBGv2ResourceDocTest / RetiredApiStandardsTest / GetScannedApiVersionsTest green (123 tests) after the registry refactor
  • Full local suite: ./run_tests_parallel.sh — 3583 tests, 0 failures (final run, after all 6 commits, alias enabled by default)

…registry

The resource-docs dispatcher serves the BG v1.3 alias (active only when
berlin_group_v1_3_alias_path is set) through its ScannedApis registration,
but APIUtil.allStaticResourceDocs never included it. Its docs carry their
own operation ids, re-derived from the alias version string, so alias
operation ids failed the getAllResourceDocs membership check used by
api-collection-endpoint creation and other operation-id lookups -- the
same gap BGv2 had before it was added to this union. Reproduced against a
running instance with the alias prop set (OBP-40048 on a valid alias
operation id) and confirmed the fix resolves it.
The alias surface is gated by berlin_group_v1_3_alias_path, which is
unset in the default test environment, so its operation-id list is
legitimately empty there -- skip the non-empty assertion for it while
still running the membership check against getAllResourceDocs.
…ocs dispatcher

Every one of its ~19 arms was `case X => resourceDocs`, unchanged --
a leftover from the pre-http4s Lift route-filter era that stopped doing
any filtering once the corresponding version moved fully onto http4s.
getResourceDocsList now feeds resourceDocs directly into
activePlusLocalResourceDocs, with identical output.
…ion from one registry

Introduce ResourceDocRegistry as the single source of truth for "which
resource docs does version X serve", replacing two independently
hand-maintained registries: ResourceDocsAPIMethods.getResourceDocsList
(the per-version dispatcher used by /resource-docs/{VERSION}/... and API
Explorer) and APIUtil.allStaticResourceDocs (the union used wherever an
operation id must be resolved). These drifted three times by hand --
Berlin Group v2, v7-only operation ids, and the Berlin Group v1.3 alias
all had to be independently added to both places, and were each missed
at least once. Deriving both from one registry map makes that class of
drift structurally impossible going forward.

Http4sBGv2 becomes a ScannedApis registrant (its apiVersion is
ConstantsBG.berlinGroupVersion2), so it is now fully convention-driven
like the other Berlin Group / UK Open Banking standards and needs no
hand-maintained entry in the registry or a special case in
ApiVersionUtils.valueOf. The global union is now deduped by operationId
-- the underlying per-version buffers legitimately overlap (each
OBP-standard aggregation repeats every older version's docs), and
consumers only ever .find or build a lookup map from the result.

ResourceDocRegistryParityTest is rewritten to iterate the registry
itself rather than a hand-typed list of standards, so a future standard
reachable by the dispatcher is covered by construction and the test's
job narrows to catching an accidental regression back to two
independently maintained registries.

Verified live against a running instance, before and after: BGv2 and
Berlin Group v1.3 alias operation ids both still resolve through
POST /my/api-collections/{name}/api-collection-endpoints. Full local
suite: 3582 tests, 0 failures.
@hongwei1 hongwei1 changed the title fix: close remaining resource-doc registry gap (BG v1.3 alias) after BGv2 fix refactor: single source of truth for resource-doc registry (closes BG v1.3 alias gap) Aug 31, 2026
…int operation id

Adds an HTTP-level regression test for the sandbox bug report this
branch started from: creating an API collection endpoint with
operation_id=BGv2-getAccountDetails now returns 201, alongside the
existing coverage for OBPv6.0.0, UK Open Banking, and Berlin Group v1.3
operation ids in the same scenario. Previously the only regression
guard for this exact operation id was the unit-level membership check
in ResourceDocRegistryParityTest; this exercises the actual endpoint.
…evel coverage

berlin_group_v1_3_alias_path could not be toggled per-test at runtime:
its ScannedApiVersion identity is captured once by ScannedApis.
versionMapScannedApis' process-wide classpath scan (a lazy val, shared
across the whole JVM/shard), which gets forced by the first unrelated
request that falls through Http4sApp's route chain -- almost always
long before any test-specific setPropsValues call. The only way to
exercise a real alias operation id end to end is to have the prop
already set before the JVM boots.

Set berlin_group_v1_3_alias_path=0.6/v1 in test.default.props (local)
and both CI workflows' generated test.default.props (build_pull_request.
yml, build_container.yml). Add a regression test in
ApiCollectionEndpointTest mirroring the existing per-standard coverage
(OBPv6.0.0/UK Open Banking/Berlin Group v1.3 canonical) for the alias's
BGv1-getPaymentInitiationStatus operation id, and pin the same
operation id in ResourceDocRegistryParityTest alongside the existing
BGv2-getAccountDetails pin.
…ift instance

The global operation-id union used to be built from the v6.0.0
aggregation, so operation ids belonging to endpoints that exist only in
v7.0.0 were absent from it and could not be added to an API collection.
That drift instance had no regression test: the OBPv6.0.0-* cases in
ApiCollectionEndpointTest pass under both the old v6-based union and the
current v7-based one, so they cannot detect it.

Pin OBPv7.0.0-getMyMetrics (v7-only -- not part of
Http4sResourceDocAggregation.v600) as a real api-collection-endpoint
request, and add the matching named pin in ResourceDocRegistryParityTest
alongside the BGv2 and Berlin Group v1.3 alias ones, so all three
historical drift instances now have both HTTP-level and registry-level
coverage.
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant