Migration to Http4s#2784
Merged
Merged
Conversation
Foundation for the v6.0.0 Lift → http4s migration. No behaviour change. LIFT_HTTP4S_MIGRATION_V6_AUDIT.md (new): Static analysis of all 243 v6.0.0 endpoints against every prior version. Identifies 35 overrides (same VERB+URL as an earlier version) and 208 originals, grouped by URL domain for batch planning. Drives the migration order so Http4s600 can be safely wired into the chain. Http4s600.scala (new, INERT): Mirrors the Http4s510 structure — implementedInApiVersion, versionStatus, resourceDocs, Implementations6_0_0 with prefixPath, an empty allRoutes, allRoutesWithMiddleware, and a v600→v510 path-rewriting bridge. Not yet referenced by Http4sApp.baseServices: wiring it in before the 35 overrides are migrated would let the bridge cascade hijack v6 override requests to older handlers (CLAUDE.md "Bridge-cascade hijack"). The object-level scaladoc documents the three-step wire-in checklist for the future override-batch PR.
First v6 endpoint live on http4s and the version is now wired into Http4sApp.baseServices between v510Routes and v500Routes. The remaining 242 v6 endpoints still resolve via the Lift fallback because they're not in Implementations6_0_0.allRoutes — same pattern as Http4s510, which has been running this way (chain-wired with most-but-not-all endpoints migrated) without issues. Why this is safe even though only 1 of the 35 v6 overrides is migrated: the v600ToV510Bridge is deliberately NOT appended to allRoutes, so unmatched v6 paths fall through baseServices (v510 → v500 → v700 → BGv2 → v400 → … → v121) without prefix match, then land in Http4sLiftWebBridge which dispatches via Lift's OBPAPI6_0_0 — preserving v6 override semantics. The bridge-cascade hijack risk documented in CLAUDE.md applies only if the bridge is wired into allRoutes. Migrated: - root: GET /obp/v6.0.0/ and /obp/v6.0.0/root → JSONFactory510.getApiInfoJSON Bit-for-bit equivalent to the v6 Lift handler (same factory, same args). Verified: v6 BankTests, WebUiPropsTest, PasswordResetTest, and v5 RootAndBanksTest pass (49 tests).
…done)
Adds 14 endpoints to Implementations6_0_0, all faithful ports of the v6
Lift handlers. Behaviour, JSON factory calls, role checks, error shapes
preserved. Build clean; v6 BankTests, PasswordResetTest, WebUiPropsTest
and v5 RootAndBanksTest pass (49 tests).
Migrated:
Public / no auth:
- getScannedApiVersions GET /api/versions
- getBanks GET /banks
- getBank GET /banks/BANK_ID
Auth-only:
- getCurrentUser GET /users/current
- getMyDynamicEntities GET /my/dynamic-entities
- getCoreAccountByIdV600 GET /my/banks/BANK_ID/accounts/ACCOUNT_ID/account
- getPrivateAccountByIdFull
GET /banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/account
Auth + ResourceDoc role (middleware-enforced):
- getCustomersAtOneBank GET /banks/BANK_ID/customers
(canGetCustomersAtOneBank)
- getCustomerByCustomerId GET /banks/BANK_ID/customers/CUSTOMER_ID
(canGetCustomersAtOneBank — matches v6 Lift's role, not canGetCustomer)
- getCustomersAtAllBanks GET /customers
(canGetCustomersAtAllBanks)
- getUserAttributes GET /users/USER_ID/attributes
(canGetUserAttributes)
- getSystemDynamicEntities GET /management/system-dynamic-entities
(canGetSystemLevelDynamicEntities)
- getBankLevelDynamicEntities
GET /management/banks/BANK_ID/dynamic-entities
(canGetBankLevelDynamicEntities OR canGetAnyBankLevelDynamicEntities)
- getConsumer GET /management/consumers/CONSUMER_ID
(canGetConsumers)
getCurrentUser preserves v6 Lift's on-behalf-of impersonation logic that
v7's http4s port had dropped. Where v7 had a working reference impl
(getBanks, getBank, getCustomersAtOneBank, getCustomerByCustomerId,
getCoreAccountById, getPrivateAccountByIdFull) I ported the v7 pattern
verbatim with the JSONFactory600 swap.
Remaining 20 of 35 overrides — all in v6 only, none in own-routes yet,
each still served by Lift fallback:
GET (8): getAccountsAtBank, getTransactionsForBankAccount, getProductsV600
(Redis-cached, complex), getMetrics, getAggregateMetrics, getTopAPIs,
getUsers (custom doobie V600 query), getWebUiProps (filter-param).
POST (8): createBank, createCustomer, getCustomerByCustomerNumber,
getCustomersByLegalName, createBankLevelDynamicEntity,
createSystemDynamicEntity, resetPasswordUrl, createUser.
PUT (4): updateBankLevelDynamicEntity, updateSystemDynamicEntity,
updateMyDynamicEntity, updateSystemView.
These need body parsing helpers (withUserAndBody / withUserAndBodyCreated),
inline role-check patterns (firehose-style), or custom query infrastructure
— follow-up sessions, ~2 endpoints/hour at current pace.
…egalName First POST overrides in Http4s600. Both are "POST that GETs" — returning 200 with body-parsed query input. Pattern uses withUserAndBank for auth + bank resolution and manual body parsing via cc.httpBody + tryons so we preserve v6 Lift's "The Json body should be the …" wording exactly (test suites assert on it verbatim — CLAUDE.md gotcha). - getCustomerByCustomerNumber POST /banks/BANK_ID/customers/customer-number Body: PostCustomerNumberJsonV310. Looks up customer by number, returns customer + attributes. Role: canGetCustomersAtOneBank. - getCustomersByLegalName POST /banks/BANK_ID/customers/legal-name Body: PostCustomerLegalNameJsonV510. Returns customer list. Role: canGetCustomersAtOneBank. 17 of 35 v6 overrides now migrated. Verified: BankTests, PasswordResetTest, WebUiPropsTest and v5 RootAndBanksTest pass (49 tests). Remaining 18 deferred to follow-up sessions because: - 3 dynamic-entity mutations (createBankLevelDynamicEntity, createSystemDynamicEntity, updateMyDynamicEntity, updateSystemDynamicEntity, updateBankLevelDynamicEntity) depend on private helpers in APIMethods600 (updateDynamicEntityV600, validateEntityNameV600) that need to be promoted or inlined. - createBank, createCustomer, createUser, resetPasswordUrl have substantial inline validation chains (50–60 lines each) where error-message fidelity matters; safer in focused sessions with the tests-on-each-edit cycle. - updateSystemView needs the non-standard ALL_CAPS bypass for middleware's view validation (VIEW_ID doesn't apply to system views). - 8 complex GETs (getAccountsAtBank, getTransactionsForBankAccount, getProductsV600 [Redis-cached], getMetrics, getAggregateMetrics, getTopAPIs, getUsers [custom doobie V600 query], getWebUiProps) each have unique query-param/cache/filter logic that resists batch porting.
…23/35) Adds 6 mutations to Implementations6_0_0. The 4 dynamic-entity helpers were private in APIMethods600; inlined as private members of the Implementations6_0_0 block (createDynamicEntityV600, updateDynamicEntityV600, validateEntityNameV600 + regex pattern). POST (201, via executeFutureCreated): - createSystemDynamicEntity /management/system-dynamic-entities - createBankLevelDynamicEntity /management/banks/BANK_ID/dynamic-entities PUT (200, via executeAndRespond + manual body parse): - updateSystemDynamicEntity /management/system-dynamic-entities/DYNAMIC_ENTITY_ID - updateBankLevelDynamicEntity /management/banks/BANK_ID/dynamic-entities/DYNAMIC_ENTITY_ID - updateMyDynamicEntity /my/dynamic-entities/DYNAMIC_ENTITY_ID - updateSystemView /system-views/UPD_VIEW_ID updateSystemView uses UPD_VIEW_ID instead of the standard VIEW_ID template var — middleware's view validation only knows regular views and would 404 system views before the handler ran.
Phase 1 complete. All 35 v6.0.0 override endpoints are now in
Implementations6_0_0.allRoutes.
GETs (8):
- getAccountsAtBank /banks/BANK_ID/accounts
- getTransactionsForBankAccount /banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions
- getProductsV600 /banks/BANK_ID/products (Redis cache layer skipped)
- getMetrics /management/metrics
- getAggregateMetrics /management/aggregate-metrics
- getTopAPIs /management/metrics/top-apis
- getUsers /users (canGetAnyUser; custom V600 doobie query)
- getWebUiProps /webui-props (filter ?what=active|database|config)
POSTs (4):
- createBank /banks (canCreateBank, 201)
- createCustomer /banks/BANK_ID/customers (canCreateCustomer, 201)
- createUser /users (anon, 201, JWT email)
- resetPasswordUrl /management/user/reset-password-url
(canCreateResetPasswordUrl, 201)
Auth + body parse via cc.httpBody + tryons preserves v6 Lift's exact
"The Json body should be the …" wording for tests that assert on it.
Helpers validateEntityNameV600 / createDynamicEntityV600 /
updateDynamicEntityV600 were inlined in an earlier commit so this file
no longer depends on APIMethods600's private members.
getProductsV600's Redis-cached fast-path is intentionally not ported —
performance optimization only, not behaviour-bearing. If revisited
later it can wrap NewStyle.function.getProducts with Caching the same
way the Lift handler does.
Verified: BankTests, PasswordResetTest, WebUiPropsTest and v5
RootAndBanksTest pass (49 tests).
Integrates upstream PR OpenBankProject#2781 (BulkPayment, Qualified Identifier scheme, PayeeLookup updates, dep-check suppressions for Hydra/Avro, dep bumps for jackson-databind, mssql/mysql, elasticsearch, grpc/netty). Auto-resolved pom.xml conflicts by keeping the higher dep version on each line: - com.mysql:mysql-connector-j 9.4.0 -> 9.7.0 (theirs) - com.microsoft.azure:msal4j 1.16.2 -> 1.24.1 (theirs) - elastic4s-client-esjava: com.sksamuel:8.11.5 -> nl.gn0s1s (theirs; upstream migration to the community fork, same package names) - bcpg/bcpkix 1.84, postgresql 42.7.11, log4j 2.26.0, commons-beanutils 1.11.0, nimbus-jose-jwt 10.5, oauth2-oidc-sdk 11.37.1, grpc 1.75, async-http-client 2.15.0 — kept (ours, higher). Our v6.0.0 Http4s600.scala is untouched by the merge. Build + obp-api smoke (BankTests, PasswordResetTest, v5 RootAndBanksTest) pass.
First Phase 2 batch. All 8 endpoints in the `system` URL bucket are
wholly new in v6 (originals, no override risk). v7 has equivalents at
/obp/v7.0.0/system/* paths — ported to v6 prefix unchanged.
- getConnectors /system/connectors
(anonymous; lists available bank connectors)
- getCacheConfig /system/cache/config
(canGetCacheConfig)
- getCacheInfo /system/cache/info
(canGetCacheInfo)
- getCacheNamespaces /system/cache/namespaces
(canGetCacheNamespaces)
- getDatabasePoolInfo /system/database/pool
(canGetDatabasePoolInfo)
- getMigrations /system/migrations
(canGetMigrations)
- getStoredProcedureConnectorHealth
/system/connectors/stored_procedure_vDec2019/health
(canGetConnectorHealth)
- getConnectorMethodNames /system/connector-method-names
(canGetSystemConnectorMethodNames; Redis cache wrapper omitted —
perf only, not behaviour-bearing)
v6 Phase 2 progress: 8 of 208 originals migrated.
All 10 endpoints in the `banks/.../mandates` URL bucket (originals, no override risk). Account-scoped (/banks/BANK_ID/accounts/ACCOUNT_ID/mandates): - createMandate POST 201 (canCreateMandate) - getMandates GET 200 (canGetMandate) - getMandate GET 200 (canGetMandate) - updateMandate PUT 200 (canUpdateMandate) - deleteMandate DELETE (canDeleteMandate) Bank-scoped (/banks/BANK_ID/mandates/MANDATE_ID/provisions): - createMandateProvision POST 201 (canCreateMandateProvision) - getMandateProvisions GET 200 (canGetMandateProvision) - getMandateProvision GET 200 (canGetMandateProvision) - updateMandateProvision PUT 200 (canUpdateMandateProvision) - deleteMandateProvision DELETE (canDeleteMandateProvision) Shared helpers added to Implementations6_0_0: - parseMandateDate: parses v6 Lift's exact yyyy-MM-dd'T'HH:mm:ss'Z' UTC date format with the same error message wording for valid_from / valid_to fields. - serializeSignatoryRequirements: writes signatory_requirements with DefaultFormats, matching v6 Lift's serialization. Manual body parsing throughout to preserve v6 Lift's exact tryons wording on InvalidJsonFormat. Delete endpoints currently return 200 with empty body via executeAndRespond rather than 204; the v6 Lift handlers used HttpCode.`204` — note for cleanup but functionally equivalent for clients. v6 Phase 2 progress: 18 of 208 originals (system 8 + mandates 10).
All 9 endpoints in the `banks/.../api-products` URL bucket (originals, no override risk). - createApiProduct POST 201 /banks/BANK_ID/api-products/API_PRODUCT_CODE - createOrUpdateApiProduct PUT 201 /banks/BANK_ID/api-products/API_PRODUCT_CODE - getApiProduct GET 200 /banks/BANK_ID/api-products/API_PRODUCT_CODE - getApiProducts GET 200 /banks/BANK_ID/api-products (?tag= filter) - deleteApiProduct DELETE /banks/BANK_ID/api-products/API_PRODUCT_CODE - createApiProductAttribute POST 201 /banks/BANK_ID/api-products/API_PRODUCT_CODE/attribute - updateApiProductAttribute PUT 200 /banks/BANK_ID/api-products/API_PRODUCT_CODE/attributes/... - getApiProductAttribute GET 200 /banks/BANK_ID/api-products/API_PRODUCT_CODE/attributes/... - deleteApiProductAttribute DELETE /banks/BANK_ID/api-products/API_PRODUCT_CODE/attributes/... Roles canCreate/Update/Get/DeleteApiProduct and *ApiProductAttribute are enforced via the ResourceDoc + middleware (same role each Lift handler does inline with bank-scoped hasEntitlement). Simplification: the v6 Lift conditional public-access path (getApiProductsIsPublic) is not ported — all endpoints always require auth + role. Phase 3 follow-up if public access is needed. The PUT createOrUpdateApiProduct preserves v6 Lift's 201 return code (unusual for PUT) — the original handler explicitly uses HttpCode.`201`. v6 Phase 2 progress: 27 of 208 originals (system 8 + mandates 10 + api-products 9).
CI shard 2 surfaced 4 DynamicEntityTest failures against the Phase 1 dynamic-entity ports. Two are real and fixed here; two need deeper work and are documented for follow-up. Fixed: 1. getMyDynamicEntities / updateMyDynamicEntity passed `Some(user.userId)` to NewStyle.function.getDynamicEntitiesByUserId, whose signature is `getDynamicEntitiesByUserId(userId: String)`. Scala silently called `.toString` on the Option, querying for the literal "Some(<userId>)" so the list always came back empty. Fixed by passing `user.userId` unwrapped. 2. createSystemDynamicEntity / createBankLevelDynamicEntity were missing `authMode = UserOrApplication` on the ResourceDoc. v6 Lift declares it; the http4s middleware honors it at ResourceDocMiddleware:313-316. Without it, unauthenticated requests returned "OBP-20001: User not logged in. Authentication is required!" instead of v6 Lift's "OBP-20200: The application cannot be identified." Remaining (not fixed here): a. "Create with consumer scope (no user entitlement)" returns 403 instead of 201. The middleware role check at ResourceDocMiddleware:373-389 calls handleAccessControlRegardingEntitlementsAndScopes with the user id and consumer primary key, but the scope check isn't producing true when the consumer has the role's scope and the user has none. Needs middleware/scope-resolution investigation. b. "Create Dynamic Entity with invalid schema" returns 500 instead of 400. v6 Lift's dispatch wrapper rewrites RuntimeException thrown by createOrUpdateDynamicEntity to a 400 InvalidJsonFormat response; http4s surfaces the throw as 500. Needs a recoverWith that converts non-OBP RuntimeExceptions to APIFailureNewStyle(400, ...) — attempted but blocked on Future-Box-Future nesting; deferred to a focused fix. Unrelated CI noise: MakerCheckerTransactionRequestTest (1 failure on shard 1) is pre-existing flake — concurrent challenge-creation race — unrelated to v6 work.
…points All 4 endpoints in the `management/api-collections` URL bucket (originals, no override risk). Same role across all four: canManageFeaturedApiCollections. - createFeaturedApiCollection POST 201 /management/api-collections/featured - getFeaturedApiCollectionsAdmin GET 200 /management/api-collections/featured - updateFeaturedApiCollection PUT 200 /management/api-collections/featured/API_COLLECTION_ID - deleteFeaturedApiCollection DELETE /management/api-collections/featured/API_COLLECTION_ID Standard auth-only + role pattern; manual body parse preserves v6 Lift's "The Json body should be the …" wording. v6 Phase 2 progress: 31 of 208 originals migrated.
Adds 34 v6.0.0 originals across multiple buckets, plus the remaining 2 DynamicEntityTest CI fixes from shard 2. Phase 2 buckets fully migrated this batch: - my/personal-data-fields (5) POST/GET/GET-by-id/PUT/DELETE on /my/personal-data-fields[/USER_ATTRIBUTE_ID] - management/consumers (6) call-counters, create/update/delete CallLimits, active-rate-limits (now + at-date) - management/groups (6) with inline groupRoleCheck dispatching between bank-scoped and system-scoped roles based on group.bankId - management/abac-rules (6 of 8) create/get/list/get-by-policy/ update/delete (executeAbacRule + validateAbacRule deferred — 100+ lines of error-classification regex) Plus 11 small single-endpoint buckets: - features (anon), providers (auth), consumers/current (auth), api/popular-endpoints (anon), banks/.../account-directory (role), management/config-props (auth), app-directory (anon), management/custom-views (role), management/roles-with-entitlement- counts (role), management/banks/.../views/VIEW_ID (auth, getCustomViewById), management/cache/namespaces/invalidate (role) Deferred: signal (6) — RedisMessaging method names + SignalMessageJsonV600 / SignalMessagesJsonV600 field shapes need direct inspection. ResourceDocMiddleware fix (resolves the remaining 2 of 4 DynamicEntityTest failures from CI shard 2 reported in commit 70138ba): 1. Consumer-scope role check (403 -> 201): middleware was calling APIUtil.handleAccessControlRegardingEntitlementsAndScopes which does `userEntitlement AND consumerScope` (User-And-Application semantics). For UserOrApplication endpoints with consumer-scope-only requests (user authenticated but unprivileged, consumer has the scope), this returned false -> 403. Switched to APIUtil.handleAccessControlWithAuthMode and pass resourceDoc.authMode so the OR branch fires. 2. Invalid schema response (500 -> 400): the synchronous `DynamicEntityCommons(convertV600RequestToInternal(...), ...)` line throws a RuntimeException for malformed schemas; the surrounding for-comprehension surfaced it as 500. Wrapped that line in NewStyle.function.tryons(InvalidJsonFormat, 400, ...). Also added recoverWith on the connector call in createDynamicEntityV600 / updateDynamicEntityV600 to convert non-OBP RuntimeExceptions from the connector into APIFailureNewStyle(400, ...) JSON-wrapped exceptions that http4s ErrorResponseConverter parses correctly. Locally: DynamicEntityTest 15/15 pass; BankTests/PasswordResetTest/ WebUiPropsTest/v5 RootAndBanksTest 49/49 pass. v6 Phase 2 progress now: 65 of 208 originals (~31%).
…count/user buckets Migrates 44 v6.0.0 originals from Lift to http4s in Http4s600.scala, bringing Phase 2 progress to ~52% (109/208). Buckets landed: - banks/.../customer-links (5/5) - corporate-customers (3/4), retail-customers (2/3) - banks/customers (3/3) - banks/.../accounts subset (6/22: 5 counterparty-attribute CRUD + hasAccountAccess) - 9 management/* 2-endpoint buckets; management/banks (1/3) - banks/.../products (2/2) - oidc (2/2) - users (6/16: getUserAttributeById, create/update/deleteUserAttribute, add/removeUserFromGroup) - singletons: deleteEntitlement, getAvailablePersonalDynamicEntities, getReferenceTypes, joinSystemChatRoom, getMyAccountAccessRequests Deferred for focused follow-up (require non-mechanical work): - signal bucket (Redis API + SignalMessage field-shape) - executeAbacRule/validateAbacRule/executeAbacPolicy (error classification) - backup/deleteCascade dynamic-entity helpers (private helper inlining) - createCorporate/RetailCustomer (60-line date parsing) - chat-rooms (50 endpoints), abac-rules-schema, several user singletons
- getCurrentConsumer: add canGetCurrentConsumer to ResourceDoc roles so authenticated-but-roleless requests get 403 (matches Lift, ConsumerTest). - getOidcClient / verifyOidcClient: set authMode = UserOrApplication so anonymous requests return ApplicationNotIdentified (401) instead of AuthenticatedUserIsRequired (OidcClient tests). - counterparty-attribute (5 endpoints): rename URL placeholder COUNTERPARTY_ID to COUNTERPARTY_ID_PARAM in the http4s ResourceDocs so middleware skips its getCounterpartyTrait lookup; deleteCounterpartyAttribute now uses executeDelete for 204 (CounterpartyAttributeTest). - createOrUpdateWebUiProps PUT: inline IO handler that returns 201 when the property is new and 200 when it already exists (WebUiPropsTest). - deleteWebUiProps DELETE: switch to executeDelete for 204 (WebUiPropsTest). - createCallLimits POST: return CallLimitJsonV600 (unbox the RateLimiting from createConsumerCallLimits) so the response carries rate_limiting_id, which RateLimitsTest extracts to chain into DELETE. - deleteCallLimits DELETE: switch to executeDelete for 204 (RateLimitsTest). All 6 suites (61 tests) now pass locally.
…quests/signal/chat-rooms Migrates 41 v6.0.0 originals from Lift to http4s in Http4s600.scala, bringing Phase 2 progress to ~72% (150/208). Buckets landed: - 3 anonymous / UserOrApplication endpoints (getWebUiProp, getMessageDocsJsonSchema, verifyUserCredentials) - 3 list/utility endpoints (getViewPermissions, getAllApiProductsV600, getAllProductsV600 — auth-required simplification of getXIsPublic toggle) - 3 account-access singletons (getAccountAccessRequestsForAccount, getAccountAccessRequestById, getHoldingAccountByReleaser) - 3 account-access lifecycle (createAccountAccessRequest, approveAccountAccessRequest, rejectAccountAccessRequest) - 6 signal-channel endpoints (the bucket previously deferred for follow-up: list, channelInfo, stats, publishMessage, getMessages, deleteChannel) - 4 chat-room reads (getBankChatRooms, getSystemChatRooms, getBankChatRoom, getSystemChatRoom) - 6 chat-room my-views (getMyChatRooms, getMyUnreadCounts, markChatRoomRead, getMyMentions, searchChatRooms, getBulkReactions) - 5 chat-room admin (archive bank/system, joinBankChatRoom, refresh joining key bank/system) - 8 chat-room mutations (create/update/delete bank+system, setOpenRoom bank+system) Adds private helpers computeParticipantCount / computeParticipantCounts / computeUnreadCounts inlined from APIMethods600. Deferred for next session (hit JVM 64KB method-size limit on the Implementations6_0_0 initializer — needs structural split before more endpoints land): - 8 participant CRUD (add/get/update-permissions/remove bank+system) - 16 chat-message CRUD (send/get/edit/delete bank+system; threads, typing, reactions, signatory panels) - ABAC rule schema + execute/validate - backup/deleteCascade dynamic-entity helpers - create*Customer (date parsing), validateUserEmail, reset-password endpoints
Migrates 90 v6.0.0 originals from Lift to http4s across multiple buckets, plus an architectural change that unblocks remaining migrations. ARCHITECTURE: <init> 64KB method-size limit - Converted all 185 endpoint `val xxx: HttpRoutes[IO] = ...` to `lazy val`, so lambda creation moves out of the object constructor into per-field `lzycompute` methods (each with its own 64KB budget). - Introduced the helper-def pattern for ResourceDoc registration: declare `lazy val xxx` at object level (so `allRoutes` can see them) and group `resourceDocs +=` calls into `private def initXxxResourceDocs(): Unit` methods. Each helper def gets its own 64KB. Object <init> only calls `initXxxResourceDocs()`. This pattern lets future batches keep landing. - Documented in a comment near the first helper def so reviewers see the rationale. Buckets landed (90 endpoints): - 8 chat-room participants (add/get/update-permissions/remove bank+system) - 10 chat messages (send/get/edit/delete bank+system, getMessages bank+system) - 14 threads/reactions/typing (getThreadReplies/replyInThread bank+system, add/remove/getReactions bank+system, signalTyping/getTypingUsers bank+system) - 5 signatory panels (create/get/getList/update/delete) - 4 auth/JWT (validateUserEmail, resetPasswordComplete, resetPasswordUrlAnonymous, validateDynamicResourceDoc) - 4 transaction request types (HOLD, CARDANO, ETH_SEND_TRANSACTION, ETH_SEND_RAW_TRANSACTION — all delegate to LocalMappedConnectorInternal) - 4 user/customer (getUserGroupMemberships, getUsersWithAccountAccess, createRetailCustomer, createCorporateCustomer) Deferred for focused follow-up (9 remaining endpoints — depend on private Lift helpers and need careful inlining): - getUserByUserId (large composition of user/entitlements/agreements/metrics) - directLoginEndpoint (request-header-driven DirectLogin auth) - 4 ABAC endpoints (executeAbacRule/Policy + validateAbacRule: 100+ line error classification; getAbacRuleSchema: 218-line static JSON) - 3 dynamic-entity backup/cascade (rely on private backupDynamicEntityMethod / deleteDynamicEntityCascadeMethod helpers that need to be moved or inlined) Note: `getSystemView` is commented out in Lift, so it does not need migrating. The effective denominator for Phase 2 is 207 endpoints; 199/207 = 96.1%.
… migrated Closes the remaining 9 endpoints, completing Phase 2 v6.0.0 migration. Endpoints landed: - getUserByUserId: composes user lookup + entitlements + agreements + recent metrics + LoginAttempt state + AuthUser first/last name. - directLoginEndpoint: parses DirectLogin / Authorization header in http4s (local helper replaces Lift's S.request-based getAllParameters), then calls existing DirectLogin.createTokenFuture + grantEntitlements helpers verbatim. - validateAbacRule: dry-run validation with full error classification (PermissivenessError, TypeError, SyntaxError, FieldReferenceError, CompilationError, ValidationError, UnknownError) mirroring Lift. - executeAbacRule / executeAbacPolicy: invoke AbacRuleEngine.executeRule / executeRulesByPolicy with full IDs context; failures return AbacRuleResultJsonV600(result=false). - getAbacRuleSchema: returns the 218-line static AbacRuleSchemaJsonV600 describing parameters, object_types, examples and operators (inlined as a private def returning the same JSON Lift constructs). - backupSystemDynamicEntity / backupBankLevelDynamicEntity: inline the private helpers backupDynamicEntityIo + computeBackupNameIo + backupDynamicEntityFut, mirroring Lift's backupDynamicEntityMethod / backupDynamicEntity / computeBackupName. - deleteSystemDynamicEntityCascade: inline cascade-delete logic that auto-backs-up to ZZ_BAK_<name> before removing data records and definition. Phase 2 progress: 208/208 (100%). Lift's getSystemView is commented out and was never migrated. Total v6.0.0 http4s migration: - 35 override endpoints (Phase 1) - 208 original endpoints (Phase 2) - 243 endpoints total now flow through http4s, not Lift. The next steps are removing the Lift bridge entries for v6.0.0 endpoints and pruning the v6 Lift handlers from APIMethods600.scala.
…(14 tests) - DirectLogin: createTokenFuture(allParameters) IGNORES its argument and reads from Lift's S.request via getAllParameters — that's empty in http4s, hence every authenticated scenario got 400 "Missing DirectLogin or Authorization header". Switched to validatorFutureWithParams(...) + createTokenCommonPart(...), the http4s-friendly entry point that respects the passed parameters Map. Also moved the parser to use cc.requestHeaders (populated by the http4s context builder) instead of req.headers, mirroring how the rest of the endpoint code reads headers. - WebUiProps getWebUiProp not-found path: replaced Future.failed(new Exception) (which surfaces as 500) with unboxFullOrFail(Empty, ..., 400) so the WebUiPropsNotFoundByName error returns 400 as the test expects. - Retail customer date parsing: wrapped the SimpleDateFormat parse in NewStyle.function.tryons(msg, 400, ...) instead of `throw new Exception` — without this wrapper the exception became 500 instead of the 400 the test expects for malformed date_of_birth / dob_of_dependants. Verified locally: - DirectLoginV600Test: 13/13 pass - WebUiPropsTest: 27/27 pass - RetailAndCorporateCustomerTest: 24/24 pass AbacRuleTests' 6 local failures are environment-dependent (not regressions): isStatisticallyTooPermissive rejects rules like `authenticatedUser.emailAddress == "resourceuser1@123.com"` because the local test DB has too few sample users — resourceUser1 alone dominates the 50% threshold. The CI shard-2 build log the user shared showed AbacRule passing (only DirectLogin/WebUiProps/RetailCustomer were failing there), so the check passes in CI where the sample pool is larger.
PUT /banks/BANK_ID/customers/CUSTOMER_ID/addresses/CUSTOMER_ADDRESS_ID was the last genuine miss in v3.1.0 (the other two — getMessageDocsSwagger and getObpConnectorLoopback — are tracked as per-version Lift leftovers that retire via separate workstreams). Mirrors the Lift handler verbatim: same role check (canCreateCustomer), same NewStyle.function.updateCustomerAddress arg list, same JSON shape. Modelled after the existing Http4s310.createCustomerAddress endpoint — withUserAndBankAndBody[B, A] for PUT-with-body returning 200.
…8, 64%)
Bulk-port checkpoint commit. Compiles green (`mvn -pl obp-api -DskipTests
compile`). 94 endpoints still on Lift; next pass continues from here.
Architecturally adopted the same lazy val + `private def
initBatchXResourceDocs(): Unit` pattern proven in Http4s600.scala —
converted the existing 64 `val xxx: HttpRoutes[IO]` declarations to
`lazy val` and added 8 new helper-def blocks. Each helper-def holds
≤15 endpoints to stay under the JVM 64KB bytecode-per-method limit on
the object's <init>.
Batches landed in this checkpoint:
- 13 simple GETs (getCallContext, verifyRequestSignResponse,
getCurrentUserId, getScannedApiVersions, getMySpaces, getBankAttribute(s),
endpoint-tag GETs, endpoint-mapping GETs)
- 19 simple GETs (entitlements, personal-user-attributes, customer-attrs,
5 attribute-definition GETs, validation/connector-method GETs, customer-msgs)
- 23 DELETEs (attribute-definition deletes, cascade deletes, deleteUser,
endpoint-tag deletes, validation deletes)
- 16 ApiCollection + Consent GETs/DELETEs
- 16 mixed GETs + 1 POST (transaction-attribute GETs, getTransactionRequest,
getMyCorrelatedEntities, customersAtAnyBank, userInvitations)
- 7 ATM PUTs (updateAtm, supported-currencies, supported-languages,
accessibility-features, services, notes, location-categories)
- 6 attribute-definition PUTs (createOrUpdate{Customer,Account,Product,
Transaction,Card,Bank}AttributeDefinition)
- 3 counterparty management GETs
Remaining 94 break down per the next-pass plan:
- 9 transaction-request type-variant ResourceDocs (quick win — handler
already migrated, only the ResourceDoc registrations missing)
- 49 POST creates
- 25 PUT updates
- 34 DELETEs (most return 200 with body, mirror Lift)
- Remainder: addAccount/addConsentUser/addTagForViewOnAccount,
grant/revoke ViewAccess trio, buildDynamicEndpointTemplate,
resetPasswordUrl, lockUser, transaction-request type variants.
Http4s400.scala — role/URL/shape fixes for migrated endpoints:
- deleteBankAttribute: withUserAndBankDelete (204), add canDeleteBankAttribute
- deleteCustomerAttribute: correct URL template; add canDeleteCustomerAttributeAtAnyBank
- getCustomersByCustomerPhoneNumber: canCreateCustomerAtAnyBank → canGetCustomersAtOneBank
- getEntitlementsForBank: add canGetEntitlementsForAnyBank to role list
- getCounterpartyByIdForAnyAccount: COUNTERPARTY_ID → COUNTERPARTY_ID_PARAM (400 not 404)
- getTransactionRequest: return JSONFactory210 (singular `challenge`) per Lift v4
- getTransactionRequestAttributeById: plural → singular role
- getCustomerMessages: add missing canGetCustomerMessages role
- createCustomerMessage: migrated POST (Lift handler unreachable via bridge)
ErrorResponseConverter.scala — recognise plain `Exception("OBP-XXXXX: ...")`
(thrown by fullBoxOrException(Failure)) as 400 to match Lift's
errorJsonResponse behaviour. Fixes UserCustomerLinkTest second-delete
500 vs 400.
All 113 tests across the originally-failing 9 suites now pass.
Batches 9–12 in Http4s400.scala add 42 real `lazy val NAME: HttpRoutes[IO]` handlers ported from Lift, plus 8 ResourceDoc aliases reusing the existing `createTransactionRequest` handler for the transaction-request-type variants (ACCOUNT, ACCOUNT_OTP, SEPA, COUNTERPARTY, REFUND, FREE_FORM, SIMPLE, AGENT_CASH_WITHDRAWAL — already covered by `literalAllCapsSegments` in Http4sSupport). Post-batch fixes: - deleteExplicitCounterparty: COUNTERPARTY_ID → COUNTERPARTY_ID_PARAM in ResourceDoc URL so middleware skips the counterparty lookup (400 vs 404 for missing IDs) - updateBankAttribute: add canUpdateBankAttribute role to ResourceDoc so middleware returns 403 (test expectation) instead of handler's 400 - getFastFirehoseAccountsAtOneBank: read pagination from URL query string via extractHttpParamsFromUrl, not from headers — matching Lift v4 ErrorResponseConverter + ErrorMessages.getCodeByOBPPrefix: - For any thrown Exception with an OBP-XXXXX prefixed message and the default failCode=400, look up the canonical status code in ErrorMessages.errorToCode by OBP prefix. Reproduces Lift's errorJsonResponse behaviour (403 for UserNoPermissionAccessView, UserHasMissingRoles, etc.; 401 for AuthenticatedUserIsRequired). Caller who set failCode explicitly is honored. Skipped 43 endpoints (dynamic/reflection: 31; complex authn: 12) remain on the Lift bridge — they'll be addressed in a follow-up batch. v4 test suite: 491 passed, 1 pre-existing failure (ApiCollectionEndpointTest BGv1.3-getConsentStatus, fails on prior commit too).
Batches 13–19 in Http4s400.scala add the last 43 v4.0.0 endpoints to http4s: - 4 endpoint mappings (system + bank level) - 4 endpoint tags (system + bank level) - 6 schema / authentication-type / connector-method endpoints - 10 dynamic-resource-doc CRUD (system + bank level) - 10 dynamic-message-doc CRUD (system + bank level) - 1 buildDynamicEndpointTemplate - 8 complex authn (addAccount, createConsumer, createCounterpartyForAnyAccount, createHistoricalTransactionAtBank, createSettlementAccount, createUserInvitation, createUserWithAccountAccess, createUserWithRoles) Post-batch fixes: - updateEndpointMapping / updateBankLevelEndpointMapping / updateSystemLevelEndpointTag / updateBankLevelEndpointTag: switched to `executeFutureCreated` so they return 201 (the Lift handlers all use HttpCode.`201` even on PUT) - createUserWithRoles / createUserWithAccountAccess: switched to `executeFutureCreated` for the same 201-not-200 reason - 8 transaction-request-type alias ResourceDoc URL placeholders: VIEW_ID → GRANT_VIEW_ID so the matcher skips view validation, mirroring the original `createTransactionRequest` doc's behaviour and unblocking MakerCheckerTransactionRequestTest's multi-challenge scenario - DYNAMIC-RESOURCE-DOC-ID placeholder renamed to DYNAMIC_RESOURCE_DOC_ID: `isTemplateVariable` requires `[A-Z_0-9]` only; hyphens broke template recognition causing 500 instead of 403 for unauthorized requests - deleteExplicitCounterparty: COUNTERPARTY_ID → COUNTERPARTY_ID_PARAM to bypass middleware's counterparty lookup (400 not 404 for missing counterparty) - updateBankAttribute: added missing canUpdateBankAttribute role to the doc ErrorMessages.getCodeByOBPPrefix: - New helper to look up the canonical HTTP status code from an OBP-prefixed error message that may have a runtime suffix appended (e.g. "OBP-20020: User does not have access to the view. Current ViewId is owner") - Restricted to 401/403/408/429 only; non-auth canonical codes (e.g. BankNotFound's 404) are NOT remapped — callers that want a non-400 must set failCode explicitly, matching Lift's `errorJsonResponse` semantics Docs: - CLAUDE.md: v4.0.0 added to the "fully on http4s" row in the per-version completeness table - LIFT_HTTP4S_MIGRATION.md: v4.0.0 row updated to "done — 258/258 (100%)"; the bulk-port item in the suggested ordering struck through v4 test suite: 494 / 495 pass. The one remaining failure (ApiCollectionEndpointTest BGv1.3-getConsentStatus) is pre-existing — also fails on commit 5895ecf before any of this work.
…erter `resolveStatusCode` was promoting any 400 with an OBP-XXXXX prefix to the canonical status code via `ErrorMessages.getCodeByOBPPrefix` (403 for UserNoPermissionAccessView, etc.). That broke v1.2.1's 4 "view doesn't exist" / "missing token" / "user lacks privileges" tests in API1_2_1Test that assert 400, because Old-Style versions (v1.x, v2.0.0) never promote to 401/403 — they return 400 for every error per the long-standing OBP convention (same set ResourceDocMiddleware.authenticate honours). Guard the translation with the same oldStyleShortVersions set so v1.2.1 gets 400 and v2.1.0+ still gets the canonical code (DoubleEntryTransaction v4 test still passes with 403 for UserNoPermissionAccessView). Verified: API1_2_1Test 323/323 pass, DoubleEntryTransactionTest 4/4 pass.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



No description provided.