Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
290 commits
Select commit Hold shift + click to select a range
fad16f3
refactor: take the tag table off Lift Mapper
hongwei1 Aug 16, 2026
cfed5c6
refactor: take the where-tag table off Lift Mapper
hongwei1 Aug 16, 2026
14eb2bc
refactor: take the transaction-image table off Lift Mapper
hongwei1 Aug 16, 2026
e1ae158
refactor: take the product-tag table off Lift Mapper
hongwei1 Aug 16, 2026
57ceee5
refactor: take the connector-trace table off Lift Mapper
hongwei1 Aug 16, 2026
ab701f9
refactor: hand the consent_item table to Flyway and drop its entity
hongwei1 Aug 16, 2026
457bb27
refactor: take the json-schema-validation table off Lift Mapper
hongwei1 Aug 16, 2026
45d7bec
fix: track the Flyway migrations that replaced the deleted Mapper ent…
hongwei1 Aug 16, 2026
a06692f
refactor: remove MappedTransactionType Lift entity; Flyway owns the s…
hongwei1 Aug 16, 2026
6bfcb4b
refactor: delete the abandoned PemUsage entity
hongwei1 Aug 16, 2026
4149226
fix: do not hand Doobie a request connection proxy that has already c…
hongwei1 Aug 16, 2026
a049163
refactor: remove MappedETag Lift entity; Flyway owns the ETag schema
hongwei1 Aug 16, 2026
91b6a0c
refactor: remove AuthenticationTypeValidation Lift entity; Flyway own…
hongwei1 Aug 16, 2026
af831cb
refactor: remove UserLocks Lift entity; Flyway owns the schema
hongwei1 Aug 16, 2026
6f344c3
refactor: remove ConnectorMethod Lift entity; Flyway owns the schema
hongwei1 Aug 16, 2026
e2d091e
refactor: remove ApiCollectionEndpoint Lift entity; Flyway owns the s…
hongwei1 Aug 16, 2026
e2d8685
refactor: remove FeaturedApiCollection Lift entity; Flyway owns the s…
hongwei1 Aug 16, 2026
3e7127c
fix: repair no-op update in consent auth context storage; move it off…
hongwei1 Aug 16, 2026
81cccdb
fix: repair no-op update in user auth context storage; move it off Li…
hongwei1 Aug 16, 2026
2a63082
refactor: remove UserInitAction Lift entity; Flyway owns the schema
hongwei1 Aug 16, 2026
a1a9802
refactor: remove AccountIdMapping Lift entity; Flyway owns the schema
hongwei1 Aug 16, 2026
c1ef803
refactor: remove TransactionIdMapping Lift entity; Flyway owns the sc…
hongwei1 Aug 16, 2026
10bd30b
refactor: remove MappedCustomerIdMapping Lift entity; Flyway owns the…
hongwei1 Aug 16, 2026
feccf98
refactor: remove MappedBankAccountData Lift entity; Flyway owns the s…
hongwei1 Aug 16, 2026
9e96784
refactor: remove ApiCollection Lift entity; Flyway owns the schema
hongwei1 Aug 16, 2026
c85815a
refactor: remove MappedBadLoginAttempt Lift entity; Flyway owns the s…
hongwei1 Aug 16, 2026
2ee8fa1
refactor: remove BankAccountRouting Lift entity; Flyway owns the schema
hongwei1 Aug 16, 2026
013704c
refactor: remove MappedCurrency and MappedFXRate Lift entities
hongwei1 Aug 17, 2026
71a9e66
refactor: remove MigrationScriptLog Lift entity; Flyway owns the schema
hongwei1 Aug 17, 2026
c306b0e
refactor: remove TransactionRequestReasons Lift entity; Flyway owns t…
hongwei1 Aug 17, 2026
8f369d4
refactor: remove ApiProductAttribute Lift entity; Flyway owns the schema
hongwei1 Aug 17, 2026
6312d42
refactor: remove MappedUserAuthContextUpdate Lift entity; Flyway owns…
hongwei1 Aug 17, 2026
cbe392c
refactor: migrate MappedCardAttribute to Doobie
hongwei1 Aug 17, 2026
25a361e
refactor: migrate AtmAttribute to Doobie
hongwei1 Aug 17, 2026
c64fab8
refactor: migrate BankAttribute to Doobie
hongwei1 Aug 17, 2026
46d275f
refactor: migrate CounterpartyAttribute to Doobie
hongwei1 Aug 17, 2026
19c15a8
refactor: migrate RegulatedEntityAttribute to Doobie
hongwei1 Aug 17, 2026
6e22e3c
refactor: migrate MappedProductAttribute to Doobie
hongwei1 Aug 17, 2026
ff26203
refactor: migrate MappedCustomerAttribute to Doobie
hongwei1 Aug 17, 2026
e5a0b9b
refactor: migrate MappedAccountAttribute to Doobie
hongwei1 Aug 17, 2026
4bfe434
refactor: migrate MappedTransactionAttribute to Doobie
hongwei1 Aug 17, 2026
5d7b885
test: add the TransactionAttribute characterization test file
hongwei1 Aug 17, 2026
a32ccc3
refactor: migrate TransactionRequestAttribute to Doobie
hongwei1 Aug 17, 2026
53b3018
refactor: delete the leftover TransactionRequestAttribute Mapper entity
hongwei1 Aug 17, 2026
10e846d
refactor: migrate MappedTaxResidence to Doobie
hongwei1 Aug 17, 2026
bca8a28
refactor: migrate CustomerLink to Doobie
hongwei1 Aug 17, 2026
4130a21
refactor: migrate CounterpartyLimit to Doobie
hongwei1 Aug 17, 2026
0d5eea0
refactor: migrate CustomerAccountLink to Doobie
hongwei1 Aug 17, 2026
5def226
refactor: migrate MappedUserCustomerLink to Doobie
hongwei1 Aug 17, 2026
58663af
refactor: migrate MappedCrmEvent to Doobie
hongwei1 Aug 17, 2026
b35a5ed
refactor: migrate UserRefreshes off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
2eedc34
refactor: remove dead examplething scaffold off Lift Mapper
hongwei1 Aug 17, 2026
7fde78d
refactor: migrate PayeeLookup off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
afd4925
refactor: migrate MetricsArchiveRun off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
8c10945
refactor: migrate OpenCorridorFeeAccrual off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
a44ceb8
refactor: migrate UtilityPaymentCallback off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
6e2fa15
refactor: migrate WebUiProps off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
ac72b13
refactor: remove dead UserScope scaffold off Lift Mapper
hongwei1 Aug 17, 2026
a997975
refactor: migrate Group off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
f6027c9
refactor: migrate Organisation off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
da98fc3
fix: constrain the id-mapping tables to one OBP id per bank reference
hongwei1 Aug 17, 2026
bd4b22c
test: stop ConcurrentBackoffCounterSelfHealTest racing its own assertion
hongwei1 Aug 17, 2026
5af690b
refactor: migrate AttributeDefinition off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
c6e5e67
refactor: migrate JobScheduler off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
ec1b620
refactor: migrate BankAccountBalance off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
b7d7651
refactor: migrate EndpointTag off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
d480b51
refactor: migrate ApiProduct off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
890cc92
refactor: migrate AmqpBankBroker off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
dfaac37
refactor: migrate ProductFee off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
09ea421
refactor: migrate MessageOutbox off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
ccf8354
refactor: migrate OpenIDConnectToken off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
881be8a
refactor: migrate UserAgreement off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
81eb940
refactor: migrate UserInvitation off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
a8cd25b
refactor: migrate MethodRouting off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
df72676
refactor: migrate AccountAccessRequest off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
91b7148
refactor: move BulkPayment and BulkBatchReference off Lift Mapper
hongwei1 Aug 17, 2026
59b4554
refactor: move the KYC family and social-media handles off Lift Mapper
hongwei1 Aug 17, 2026
263e99e
refactor: move the chat group off Lift Mapper
hongwei1 Aug 17, 2026
463ba8b
refactor: move product collections, direct debits and standing orders…
hongwei1 Aug 17, 2026
368a318
refactor: move webhooks, scopes and account applications off Lift Mapper
hongwei1 Aug 17, 2026
80b9d43
refactor: move customer addresses and entitlement requests off Lift M…
hongwei1 Aug 17, 2026
d062e81
refactor: move dependants, counterparty bespokes and challenge answer…
hongwei1 Aug 17, 2026
ad09b0e
refactor: move user attributes and regulated entities off Lift Mapper
hongwei1 Aug 17, 2026
b4f3e32
refactor: move routing schemes and ABAC rules off Lift Mapper
hongwei1 Aug 17, 2026
ffdb812
refactor: move endpoint mappings and the projection registry off Lift…
hongwei1 Aug 17, 2026
937b87f
refactor: move meetings and meeting invitees off Lift Mapper
hongwei1 Aug 17, 2026
8bb5a71
refactor: move customer messages and transaction-request-type charges…
hongwei1 Aug 17, 2026
da77504
refactor: move physical cards and PIN resets off Lift Mapper
hongwei1 Aug 17, 2026
a6ee023
refactor: move the double-entry book off Lift Mapper
hongwei1 Aug 17, 2026
7515ee1
refactor: move dynamic endpoints off Lift Mapper
hongwei1 Aug 17, 2026
82c8bba
refactor: move connector metrics off Lift Mapper
hongwei1 Aug 17, 2026
343c053
refactor: move entitlements off Lift Mapper
hongwei1 Aug 17, 2026
1d51fa1
refactor: move rate limiting off Lift Mapper
hongwei1 Aug 17, 2026
45a8310
refactor: move products off Lift Mapper
hongwei1 Aug 17, 2026
73ede64
refactor: move branches off Lift Mapper
hongwei1 Aug 17, 2026
25fcba0
refactor: move account holders off Lift Mapper
hongwei1 Aug 17, 2026
6c5da5b
refactor: move dynamic message docs off Lift Mapper
hongwei1 Aug 17, 2026
36cc166
refactor: move dynamic resource docs off Lift Mapper
hongwei1 Aug 17, 2026
970e7ae
refactor: move dynamic-entity row ACLs off Lift Mapper
hongwei1 Aug 17, 2026
77079dc
refactor: move dynamic entities off Lift Mapper
hongwei1 Aug 17, 2026
c49482c
refactor: move dynamic-entity data off Lift Mapper
hongwei1 Aug 17, 2026
ac5a5a0
refactor: move view permissions off Lift Mapper
hongwei1 Aug 17, 2026
fd0b9bd
refactor: move account access off Lift Mapper
hongwei1 Aug 17, 2026
93c8c49
refactor: move the mandate tables off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
f4b79eb
refactor: move the signing-basket tables off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
c306abc
refactor: move consentrequest off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
808aebf
refactor: move the counterparty tables off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
92ed3de
refactor: move mappedbank off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
829ac98
refactor: move mappedtransaction off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
c78af3b
refactor: move mappedtransactionrequest off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
e2d0e16
refactor: move mappedcustomer off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
ee082c4
refactor: move the metric tables off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
5f0518d
docs: record the two Doobie migration traps the full suite caught
hongwei1 Aug 17, 2026
253dfcd
refactor: move mappedconsent off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
d391576
refactor: move mappedbankaccount off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
3f0b253
refactor: move viewdefinition off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
d3ec07f
refactor: move nonce off Lift Mapper to Doobie
hongwei1 Aug 17, 2026
470d5da
refactor: move token off Lift Mapper to Doobie
hongwei1 Aug 18, 2026
8de8a87
refactor: move consumer off Lift Mapper to Doobie
hongwei1 Aug 18, 2026
01b2e93
refactor: move resourceuser off Lift Mapper to Doobie
hongwei1 Aug 18, 2026
add272e
refactor: move authuser off Lift Mapper to Doobie
hongwei1 Aug 18, 2026
1ee6f45
chore: drop the net.liftweb.mapper imports the migration left behind
hongwei1 Aug 18, 2026
1fae437
refactor: pay the mechanical Scala 3 syntax debt while still on 2.13
hongwei1 Aug 18, 2026
4c5bf0c
fix: three defects the Doobie stores' read paths carried
hongwei1 Aug 18, 2026
8bd08ac
fix: the investigation report queried a table that does not exist
hongwei1 Aug 18, 2026
a95cd1b
fix: the proxy connector skipped list payloads and threw on not-found
hongwei1 Aug 18, 2026
bbbe5f8
fix: restore five unique indexes the earliest migration scripts dropped
hongwei1 Aug 18, 2026
643c08a
fix: restore the plain indexes the earliest migration scripts dropped
hongwei1 Aug 18, 2026
4d4e0b9
fix: a NULL view flag has to read back as false, as Mapper read it
hongwei1 Aug 18, 2026
79e027e
fix: a NULL call limit reads back as the configured one, not as unlim…
hongwei1 Aug 18, 2026
b8e65d2
fix: creating a dynamic-entity record must insert, not overwrite
hongwei1 Aug 18, 2026
3dfca64
fix: a NULL column reads back the way Mapper read it, and reads back …
hongwei1 Aug 18, 2026
1237289
fix: restore the six indexes V117 left behind
hongwei1 Aug 18, 2026
821ef23
fix: baseline a pre-existing schema at the latest script, not at vers…
hongwei1 Aug 18, 2026
0aa43d2
fix: flyway.enabled defaults to true, so a deployment gets a schema
hongwei1 Aug 18, 2026
d2c9d97
fix: read every nullable column through Option, as Mapper's readers did
hongwei1 Aug 18, 2026
d0cad28
docs: record the Flyway and nullable-read traps the review turned up
hongwei1 Aug 18, 2026
e27e068
refactor: finish paying the auto-application debt, and make a new one…
hongwei1 Aug 18, 2026
381a4e1
fix: the 6-shard layout ran 51 of 87 test packages and still reported…
hongwei1 Aug 18, 2026
039a8fe
fix: a same-day re-acceptance resolves to the latest agreement, not t…
hongwei1 Aug 18, 2026
b1783fc
test: refuse to run the suite against a database that is not disposable
hongwei1 Aug 18, 2026
36308cd
feat: add the Postgres migration scripts, so a Postgres deployment ge…
hongwei1 Aug 18, 2026
f9230c4
test: run the whole suite on Postgres, and let the index check work o…
hongwei1 Aug 18, 2026
73d528a
feat: add Liquibase alongside Flyway, disabled, and pin snakeyaml aga…
hongwei1 Aug 18, 2026
9e6ecc3
feat: generate the baseline changelog, and prove it builds what the s…
hongwei1 Aug 18, 2026
97314c8
fix: a NULL fee amount 500s the listing, and the lint could not see t…
hongwei1 Aug 18, 2026
5e82882
feat: adopt an existing schema instead of rebuilding it, and never mi…
hongwei1 Aug 18, 2026
b274a36
refactor: remove Flyway; Liquibase owns the schema
hongwei1 Aug 18, 2026
002990d
test: run the whole suite on Postgres with --db=postgres
hongwei1 Aug 18, 2026
f975262
fix: default the Postgres port when the JDBC URL omits it
hongwei1 Aug 18, 2026
4e4f754
fix: recognise a wrapped lock failure, and pin the H2 keyword the cha…
hongwei1 Aug 18, 2026
5b5ecce
fix: shards race to create the surefire-reports directory they were j…
hongwei1 Aug 18, 2026
9c3b6d5
docs: record the Postgres run and the shared-.m2 hazard between check…
hongwei1 Aug 18, 2026
0e83516
fix: the disposable-database guard refused CI, where db.url is delibe…
hongwei1 Aug 19, 2026
6510427
chore: drop the comment left behind by the removed Flyway dependency
hongwei1 Aug 19, 2026
51d65af
fix: a duplicated changelog on the classpath refused to boot, and a N…
hongwei1 Aug 19, 2026
093973c
feat: build the OIDC views from the changelog, so a fresh database ca…
hongwei1 Aug 19, 2026
55638a5
fix: create the OIDC views after the legacy migrations, not with the …
hongwei1 Aug 19, 2026
b480314
refactor: move SwaggerJSONFactory's TypeTag constants to obp-commons …
hongwei1 Aug 19, 2026
5cd0014
refactor: remove code.util.reflectionUtils, dead code that would not …
hongwei1 Aug 19, 2026
e5783d7
refactor: move OptionalFieldSerializer's TypeTag constants to obp-com…
hongwei1 Aug 19, 2026
fb6357d
refactor: switch ClassScanUtils to ClassTag, drop the unused TypeTag …
hongwei1 Aug 19, 2026
a2a1930
refactor: build obp-api's own reflect Types at runtime instead of via…
hongwei1 Aug 19, 2026
4182933
refactor: drop the unused TypeTag bound on sendRequest in three conne…
hongwei1 Aug 19, 2026
2990624
fix: replace TypeTag synthesis in ConnectorEndpoints with runtime typ…
hongwei1 Aug 19, 2026
1e81781
fix: replace TypeTag synthesis in RestConnector_vMar2019 with runtime…
hongwei1 Aug 19, 2026
ce327e2
fix: replace remaining TypeTag synthesis in the connector/codegen uti…
hongwei1 Aug 19, 2026
e81adf2
fix: replace TypeTag synthesis in the connector test sources
hongwei1 Aug 19, 2026
4abb00d
chore: drop stale net.liftweb.mapper imports left over from the Mappe…
hongwei1 Aug 19, 2026
15e9e56
build: flip obp-api's compiler to Scala 3
hongwei1 Aug 19, 2026
8377ad7
test: fix test sources for the Scala 3 compiler flip
hongwei1 Aug 19, 2026
7a25ada
fix: pin scala3-library_3 to the compiler version
hongwei1 Aug 19, 2026
f627a54
fix: a same-thread reentrant lazy-val call deadlocks under Scala 3
hongwei1 Aug 19, 2026
aa31b1c
fix: recover Scala-3-compiled val/lazy-val members that isVal/isVar/i…
hongwei1 Aug 19, 2026
e781ae5
fix: OBPEnumerationBase self-reflects on its own not-yet-initialized …
hongwei1 Aug 19, 2026
4f36932
fix: json4s Scala 3 extraction - groupId move, classpath split, Scala…
hongwei1 Aug 19, 2026
14c5a8f
fix: getCallByNameValues only handled the Mapper call-by-name shape
hongwei1 Aug 19, 2026
d335e7b
fix: toOther's non-method fallback pointed at a hardcoded "attributes…
hongwei1 Aug 19, 2026
e091dbe
fix: dynamic entity list handlers cast List[DynamicEntityT] instead o…
hongwei1 Aug 19, 2026
e14aaae
fix: full-suite fallout - SwaggerTypes typeOf, getFieldValues over-ma…
hongwei1 Aug 19, 2026
23b1634
fix: ResourceDocsTechnologyTest - non-OBP reflection target, and a ge…
hongwei1 Aug 19, 2026
d74ed0a
fix: route around a scala-reflect symbol-table crash in the connector…
hongwei1 Aug 19, 2026
d736b54
fix: move Swagger field-type test fixtures out of the suite class body
hongwei1 Aug 19, 2026
a08eb44
feat: replace ToolBox with a dotc-driven DynamicScalaCompiler for Sca…
hongwei1 Aug 19, 2026
f417447
fix: exclude Connector's public vals from the dynamic-method symbol t…
hongwei1 Aug 19, 2026
0843fe8
fix: convert Commons list responses instead of casting them
hongwei1 Aug 19, 2026
f614a30
fix: route obp-commons case classes around json4s's Scala 3 readField…
hongwei1 Aug 19, 2026
2e7187e
fix: exclude synthetic setters and adapter conversions from connector…
hongwei1 Aug 19, 2026
c2d384b
chore: regenerate frozen type structure snapshot for the Scala 3 flip
hongwei1 Aug 19, 2026
b1daa81
fix: route obp-commons case class extraction around json4s's Scala 3 …
hongwei1 Aug 19, 2026
3361506
fix: scope ObpCommonsProductDeserializer's missing-field leniency to …
hongwei1 Aug 19, 2026
b502b48
chore: regenerate frozen type structure text rendering
hongwei1 Aug 19, 2026
13418d5
fix: select the primary constructor by its own flag, not by position
hongwei1 Aug 20, 2026
3bd8445
fix: select the primary constructor by matching declared fields, not …
hongwei1 Aug 20, 2026
9fa085b
chore: remove the primary-constructor diagnostic now that CI confirms…
hongwei1 Aug 20, 2026
887ab16
fix: resolve CLI-supplied paths before using them in the migration sc…
hongwei1 Aug 20, 2026
afed325
fix: suppress SonarCloud's path-traversal rule on the two dev-tooling…
hongwei1 Aug 20, 2026
de33106
fix: v_oidc_users view cannot be upgraded on a database from the lega…
hongwei1 Aug 20, 2026
e37141e
fix: v7.0.0 swagger docs 400 on a class scala-reflect cannot own-chain
hongwei1 Aug 20, 2026
2647fcd
fix: a NULL guard for a lazy-val deadlock also disabled log masking
hongwei1 Aug 20, 2026
e03870c
fix: constructor and field selection still nondeterministic for one s…
hongwei1 Aug 20, 2026
93887cf
fix: narrow the erasure-tolerant type check to AnyVal targets
hongwei1 Aug 20, 2026
9f58b03
refactor: drop two dead duplicate type checks in JsonSchemaGenerator
hongwei1 Aug 20, 2026
948fb13
fix: enumerate dynamic connector methods by shape, not just by name
hongwei1 Aug 20, 2026
d198732
perf: cache constructor reflection in the obp-commons JSON codec
hongwei1 Aug 20, 2026
3b4f2c4
refactor: fold Converter into ConverterWithType
hongwei1 Aug 20, 2026
e3d4e2c
perf: hoist a repeated Connector type lookup out of a filter predicate
hongwei1 Aug 20, 2026
1e99492
fix: bootstrapPatterns' credential coverage stopped at 4 categories
hongwei1 Aug 20, 2026
71a9a02
fix: getPrimaryConstructor's tie-break still depended on alternatives…
hongwei1 Aug 20, 2026
bb0cf94
fix: check AnyVal structurally instead of by a fixed name list
hongwei1 Aug 20, 2026
9ff007b
fix: the new bootstrap key pattern over-matched non-credential logging
hongwei1 Aug 20, 2026
307d15e
doc: record getPrimaryConstructor's parameter-name-only comparison limit
hongwei1 Aug 20, 2026
55b9a14
fix: bootstrapPatterns' key prefix list missed a real one - consumer_key
hongwei1 Aug 20, 2026
2ff6d96
doc: stop duplicating the bootstrapPatterns key-prefix list in two co…
hongwei1 Aug 20, 2026
3dd2aa1
test: cover GET /obp/v3.0.0/my/entitlements
hongwei1 Aug 20, 2026
cfb55aa
fix: sandbox import rejected two banks legitimately sharing an IBAN
hongwei1 Aug 21, 2026
c198b27
fix: a legacy NULL column no longer fails the whole listing query
hongwei1 Aug 24, 2026
a730bce
fix: three test suites were assigned to no shard and never ran
hongwei1 Aug 24, 2026
222622c
fix: the views the request paths read are created on a default deploy…
hongwei1 Aug 24, 2026
364de65
fix: launch the container with -cp, so runtime compilation has a clas…
hongwei1 Aug 24, 2026
767e338
fix: drop the view before creating it, so an upgrade can boot
hongwei1 Aug 24, 2026
b5d8b8f
fix: Option of a value type is published as that value type again
hongwei1 Aug 24, 2026
2e433f2
fix: check the whole published swagger surface, not just its top layer
hongwei1 Aug 24, 2026
f83546d
fix: convert a connector result to its Commons type, never cast it
hongwei1 Aug 24, 2026
946ef94
fix: CI verifies the Postgres schema instead of silently skipping it
hongwei1 Aug 24, 2026
8ba3667
fix: one update path for every state a database can boot in
hongwei1 Aug 24, 2026
ca3aaaf
fix: the frozen type contract records what a field is, not what erasu…
hongwei1 Aug 24, 2026
b629103
fix: de-duplicate the two natural-key tables in the changelog, portably
hongwei1 Aug 24, 2026
92f0465
fix: a card's absent networks/cvv/brand read back as absent, not as e…
hongwei1 Aug 24, 2026
b20d2a9
fix: the frozen-type refiner visits every field, once per object
hongwei1 Aug 24, 2026
bb46aa5
fix: the Commons-cast lint ignores a // inside a string literal
hongwei1 Aug 24, 2026
a6bb063
docs: launch with -cp everywhere, not java -jar
hongwei1 Aug 24, 2026
404683b
docs: attach bringUpToDate's scaladoc to bringUpToDate
hongwei1 Aug 24, 2026
b846317
fix: bind the metrics filter values instead of splicing them into SQL
hongwei1 Aug 24, 2026
42823bc
refactor: one filter extractor for the three metrics reads
hongwei1 Aug 24, 2026
e4ea86b
fix: a locked account can still log in through OIDC
hongwei1 Aug 25, 2026
107919d
fix: refuse to compile user-supplied Scala when the sandbox enforces …
hongwei1 Aug 25, 2026
125950a
fix: the dynamic-code dependency scan was gated on a diagnostic prop,…
hongwei1 Aug 25, 2026
cfba6c4
fix: consents.sca.enabled=false accepted any SCA answer in production…
hongwei1 Aug 25, 2026
84c4ef9
fix: authuser email lost MappedEmail's normalisation, and an unlinked…
hongwei1 Aug 25, 2026
b71a853
fix: the OIDC view honoured only half of the lockout
hongwei1 Aug 25, 2026
e9ca511
docs: retract 125950aa2's cause for the json4s failures; the scoping …
hongwei1 Aug 25, 2026
0e0fcbb
fix: revert cfb55aa6a - an IBAN is globally unique, and the payment p…
hongwei1 Aug 25, 2026
c341523
docs: upgrade note for dynamic Scala code on JDK 24 and later
hongwei1 Aug 25, 2026
15fcff4
test: import the shipped sandbox fixture, which nothing was exercising
hongwei1 Aug 25, 2026
4bd065a
fix: getValues saw nothing on a Scala 3 object, so every allFields wa…
hongwei1 Aug 25, 2026
6606b61
test: guard that every API version actually registers its resource docs
hongwei1 Aug 25, 2026
1e83d6b
merge: bring in origin/develop, porting its Mapper work onto the Doob…
hongwei1 Aug 28, 2026
f504f0c
fix: a malformed escape in a method body escaped create instead of fa…
hongwei1 Aug 29, 2026
ddef1ca
fix: the new digest table was never cleared between test classes
hongwei1 Aug 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
12 changes: 11 additions & 1 deletion .github/Dockerfile_PreBuild
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,20 @@ COPY /obp-api/target/lib /app/lib
COPY /obp-api/target/obp-api.jar /app/obp-api.jar
WORKDIR /app
USER obp
# -cp, not -jar. The dependencies live in /app/lib and reach an ordinary classloader either way,
# via the jar manifest's Class-Path - but a manifest Class-Path never appears in the
# `java.class.path` system property, and under `-jar` that property is the single jar and nothing
# else. Two things read it directly to build a compiler classpath at runtime: our own
# DotcScalaCompiler (dynamic endpoints, dynamic connector methods, ABAC rules) and, inside json4s,
# the ScalaSigReader that resolves Scala 3 case-class field types. Both then fail with
# "Could not find package scala from compiler core libraries" / an unhandled staging-compiler
# error, on a server that otherwise boots and looks healthy. Reproduced on this image: with -jar,
# POST /obp/v2.1.0/sandbox/data-import returned 500 from the staging compiler; with the -cp form
# below and nothing else changed, 201.
ENTRYPOINT ["java", \
"--add-opens", "java.base/java.lang=ALL-UNNAMED", \
"--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", \
"--add-opens", "java.base/java.io=ALL-UNNAMED", \
"--add-opens", "java.base/java.util=ALL-UNNAMED", \
"--add-opens", "java.base/java.util.concurrent=ALL-UNNAMED", \
"-jar", "/app/obp-api.jar"]
"-cp", "/app/obp-api.jar:/app/lib/*", "bootstrap.http4s.Http4sServer"]
89 changes: 89 additions & 0 deletions .github/scripts/check_changelog_data_migrations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""The de-duplications the changelog carries by hand must stay in it.

`liquibase generateChangeLog` takes a snapshot of a schema, and a statement that ran once and left
no trace in the catalogue is invisible to it. Two of the Flyway scripts deleted duplicate rows
before creating a unique index, and reverse-generating the changelog kept the indexes and dropped
the DELETEs that made them creatable. They were written back into
db/changelog/db.changelog-dedup.yaml by hand.

Nothing else would notice them going missing again. The equivalence checks that guarded the
generation compared schemas, and both built empty databases, where a de-duplication is a no-op
either way; the loss only shows up on a real database that holds duplicates, at the moment the
unique index fails to build. So the statements are frozen here, verbatim as they stood in the
Flyway scripts that are now deleted - V057 (the three internal id-mapping tables) and V116 (five
tables whose unique index the earliest migrations left out).

Freezing rather than reading them from somewhere is the point: there is no longer another copy to
compare against, and a check that derives its expectation from the file it is checking would pass
whatever that file said.

DedupChangesetsTest is the other half - it runs the changesets against a table that actually holds
duplicates. This one only asserts they are present.
"""
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
CHANGELOG_DIR = ROOT / "obp-api/src/main/resources/db/changelog"

EXPECTED = [
# Boot's own de-duplication, moved into the changelog: it ran after the index it existed to
# precede, and named a table that does not exist. Unlike the eight below these carry no dbms
# restriction, so they use the ROW_NUMBER() derived-table form MySQL's ERROR 1093 permits -
# see the changelog file's header.
"delete from mappedentitlement where id in ( select id from ( select id, row_number() over ( partition by mbankid, muserid, mrolename order by id asc) as rn from mappedentitlement where mbankid is not null and muserid is not null and mrolename is not null ) tmp where rn > 1 )",
"delete from mapperaccountholders where id in ( select id from ( select id, row_number() over ( partition by user_c, accountbankpermalink, accountpermalink order by id asc) as rn from mapperaccountholders where user_c is not null and accountbankpermalink is not null and accountpermalink is not null ) tmp where rn > 1 )",
# V057: accountidmapping
"delete from accountidmapping where maccountplaintextreference is not null and id not in ( select min(id) from accountidmapping where maccountplaintextreference is not null group by maccountplaintextreference )",
# V057: mappedcustomeridmapping
"delete from mappedcustomeridmapping where mcustomerplaintextreference is not null and id not in ( select min(id) from mappedcustomeridmapping where mcustomerplaintextreference is not null group by mcustomerplaintextreference )",
# V057: transactionidmapping
"delete from transactionidmapping where transactionplaintextreference is not null and id not in ( select min(id) from transactionidmapping where transactionplaintextreference is not null group by transactionplaintextreference )",
# V116: mappedatm
"delete from mappedatm where mbankid is not null and matmid is not null and id not in ( select min(id) from mappedatm where mbankid is not null and matmid is not null group by mbankid, matmid )",
# V116: mappedcomment
"delete from mappedcomment where apiid is not null and id not in (select min(id) from mappedcomment where apiid is not null group by apiid)",
# V116: mappedtag
"delete from mappedtag where tagid is not null and id not in (select min(id) from mappedtag where tagid is not null group by tagid)",
# V116: mappedtransactionimage
"delete from mappedtransactionimage where imageid is not null and id not in ( select min(id) from mappedtransactionimage where imageid is not null group by imageid )",
# V116: consent_item
"delete from consent_item where consent_item_id is not null and id not in ( select min(id) from consent_item where consent_item_id is not null group by consent_item_id )",
]


def normalise(sql: str) -> str:
"""Collapse whitespace, so YAML indentation does not count as a difference."""
return re.sub(r"\s+", " ", sql).strip().rstrip(";").lower()


def main() -> int:
if not CHANGELOG_DIR.is_dir():
print(f"check_changelog_data_migrations: {CHANGELOG_DIR} does not exist", file=sys.stderr)
return 1

changelog = "\n".join(p.read_text() for p in sorted(CHANGELOG_DIR.rglob("*.yaml")))
haystack = normalise(changelog)

missing = [stmt for stmt in EXPECTED if stmt not in haystack]

print(f"check_changelog_data_migrations: {len(EXPECTED)} de-duplication statement(s) expected, "
f"{len(EXPECTED) - len(missing)} present in the changelog")
if missing:
print("", file=sys.stderr)
print("These statements are missing from the changelog:", file=sys.stderr)
for stmt in missing:
print(f" {stmt[:150]}", file=sys.stderr)
print("", file=sys.stderr)
print("They belong in db/changelog/db.changelog-dedup.yaml as `sql` changesets, ordered "
"before the createIndex they clear the way for. Without them a unique index cannot "
"be built on a database that already holds duplicate rows - which is every existing "
"deployment whose constraint was missing.", file=sys.stderr)
return 1
return 0


if __name__ == "__main__":
sys.exit(main())
146 changes: 146 additions & 0 deletions .github/scripts/check_changelog_preconditions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#!/usr/bin/env python3
"""Every baseline changeset must decide for itself whether its object is already there.

`LiquibaseSchemaSetup.bringUpToDate` is a plain `update`, for every state a database can be in when
the application boots - empty, or brought by an existing deployment with tables and no Liquibase
record, or left half-way by a start that was killed. What makes one code path right for all three is
that each changeset in the baseline carries `not tableExists` / `not indexExists` with
`onFail: MARK_RAN`: it records itself without running when its object exists, and runs when it does
not.

Take those away and both of the failures they replaced come back. A blanket `changeLogSync` marks
the de-duplications and the unique indexes they clear the way for as applied on the strength of the
tables being present - and Schemifier never created those indexes, which is why V057 and V116
existed, so the databases that needed them were exactly the ones that skipped them. And a sync
commits row by row, so a start killed during one leaves a partial DATABASECHANGELOG that sent the
next start into a plain `update` over objects that already existed:
`MigrationFailedException ... Index "METRIC_CONSUMERID" already exists`, on that start and every one
after it.

The baseline is generated (scripts/GenerateChangelog.java) and normalised
(scripts/normalise_generated_changelog.py, which inserts these). A regeneration that lost the
insertion step would produce a changelog that looks right, passes every fresh-database test in the
suite, and breaks only on the upgrades this exists to serve - so the invariant is checked here
rather than left to be noticed later.

Preconditions are not part of a changeset's checksum - ChangeSet.generateCheckSum reads only the
changes and the sql visitors - so this costs nothing on databases that have already run them.
"""
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
CHANGELOG_DIR = ROOT / "obp-api/src/main/resources/db/changelog"

# Every changelog that creates or alters schema. The view changelogs are deliberately absent:
# they use createView with replaceIfExists under runOnChange, which is idempotent by
# construction, and db.changelog-dedup.yaml has its own guard
# (check_changelog_data_migrations.py) because its DELETEs cannot be expressed this way.
# Listing them rather than globbing so that adding a changelog is a decision, not an accident -
# a new schema file has to be named here, which is the moment to ask which guard it needs.
SCHEMA_CHANGELOGS = [
CHANGELOG_DIR / "db.changelog-baseline.yaml",
CHANGELOG_DIR / "db.changelog-provenance.yaml",
]


def changesets(text):
"""Each changeset as (id, body), split on the `- changeSet:` marker.

The indentation is not fixed: the generated baseline puts `- changeSet:` at column 0, while a
hand-written changelog nests it under `databaseChangeLog:`. Anchoring on column 0, as this did,
silently matched nothing in the second shape - the guard reported success over a file it had
not read. Accept either.
"""
parts = re.split(r"(?m)^[ \t]*- changeSet:\n", text)[1:]
for body in parts:
m = re.search(r"^\s*id: (\S+)$", body, re.M)
yield (m.group(1) if m else "<unnamed>"), body


def main():
missing = [c for c in SCHEMA_CHANGELOGS if not c.exists()]
if missing:
for c in missing:
print(f"check_changelog_preconditions: {c} not found", file=sys.stderr)
return 1

problems = []
checked = 0

for changelog in SCHEMA_CHANGELOGS:
where = changelog.name
for cs_id, body in changesets(changelog.read_text()):
cs_id = f"{where}::{cs_id}"
checked += 1
creates_table = "- createTable:" in body
creates_index = "- createIndex:" in body
adds_column = "- addColumn:" in body
if not (creates_table or creates_index or adds_column):
problems.append(f"{cs_id}: none of createTable / createIndex / addColumn - this check "
f"does not know what precondition it needs; teach it, do not skip it")
continue

if "preConditions:" not in body:
problems.append(f"{cs_id}: no preConditions block")
continue
if "onFail: MARK_RAN" not in body:
problems.append(f"{cs_id}: precondition does not say onFail: MARK_RAN")
if not re.search(r"^\s*- not:$", body, re.M):
problems.append(f"{cs_id}: precondition is not negated - it must fire when the object "
f"is ABSENT")

table = re.search(r"^\s*tableName: (\S+)$", body, re.M)
if creates_index:
index = re.search(r"^\s*indexName: (\S+)$", body, re.M)
if not re.search(r"^\s*- indexExists:$", body, re.M):
problems.append(f"{cs_id}: createIndex needs an indexExists precondition")
elif index is None:
problems.append(f"{cs_id}: createIndex has no indexName to check")
elif body.count(f"indexName: {index.group(1)}") < 2:
problems.append(f"{cs_id}: the precondition names an index other than "
f"{index.group(1)}")
elif adds_column:
# A column cannot be checked with tableExists - the table is there either way. The
# precondition has to name one of the columns the changeset adds, so a database that
# already went through this change marks it run instead of failing on a duplicate.
if not re.search(r"^\s*- columnExists:$", body, re.M):
problems.append(f"{cs_id}: addColumn needs a columnExists precondition")
else:
guarded = re.search(r"^\s*columnName: (\S+)$", body, re.M)
added = set(re.findall(r"\{name: ([a-z_]+),", body))
if guarded is None:
problems.append(f"{cs_id}: columnExists has no columnName to check")
elif added and guarded.group(1) not in added:
problems.append(f"{cs_id}: the precondition checks {guarded.group(1)}, which "
f"is not among the columns this changeset adds")
else:
if not re.search(r"^\s*- tableExists:$", body, re.M):
problems.append(f"{cs_id}: createTable needs a tableExists precondition")
elif table is None:
problems.append(f"{cs_id}: createTable has no tableName to check")
elif body.count(f"tableName: {table.group(1)}") < 2:
problems.append(f"{cs_id}: the precondition names a table other than "
f"{table.group(1)}")

print(f"check_changelog_preconditions: {checked} schema changeset(s) checked, "
f"{len(problems)} without a usable existence precondition")
if problems:
print("", file=sys.stderr)
for p in problems[:40]:
print(f" {p}", file=sys.stderr)
if len(problems) > 40:
print(f" ... and {len(problems) - 40} more", file=sys.stderr)
print("", file=sys.stderr)
print("Each schema changeset needs `preConditions: [onFail: MARK_RAN, not: "
"[tableExists|indexExists: <the object it creates>]]`. "
"scripts/normalise_generated_changelog.py inserts them; if the changelog was "
"regenerated without it, re-run the normaliser rather than adding them by hand.",
file=sys.stderr)
return 1
return 0


if __name__ == "__main__":
sys.exit(main())
104 changes: 104 additions & 0 deletions .github/scripts/check_no_blind_commons_casts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""A connector result must be converted to its Commons type, never cast to it.

`list.asInstanceOf[List[XCommons]]` compiles and does nothing at runtime - the element type is
erased, so the cast never checks anything. What it does is give the compiler licence to insert a
checkcast at the first element access, and to serialize whatever the elements actually are. The
premise it rests on - "the provider only ever constructs XCommons" - stopped being true when the
providers moved to Doobie and started returning their own row types implementing the same trait
(`ProductAttributeRow`, `CardAttributeRow`, ...).

That is not hypothetical. Four sites failed exactly this way and were fixed in
`fix: convert Commons list responses instead of casting them`: management/method_routings,
management/endpoint-mappings, management/banks/BANK_ID/cards/CARD_ID and management/webui_props
all threw ClassCastException instead of serving a response.

Every XCommons companion extends Converter/ConverterWithType, whose `toCommonsList` does the
conversion this cast pretends to do. Use it:

data = XCommons.toCommonsList(response) // converts
data = response.asInstanceOf[List[XCommons]] // lies

Scope is deliberately `List[...Commons]`. A cast to a list of something that is not a Commons type
is a different question - it has no Converter to reach for and no trait/implementation split behind
it - so it is left alone rather than swept in here.
"""
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
SOURCE_ROOTS = [ROOT / "obp-api/src/main/scala", ROOT / "obp-commons/src/main/scala"]

CAST = re.compile(r"asInstanceOf\[List\[\s*([A-Za-z0-9_.]*Commons)\s*\]\]")


def code_of(line):
"""The line with comment content removed - the pattern is about code, and the comment
explaining why the pattern is banned necessarily contains it.

String-aware: a `//` inside a string literal ("http://...") must not truncate the line, or a
cast written after such a literal is never seen. Block-comment interiors (` * ...`) are dropped
wholesale; a `/*` opener keeps what precedes it. Line-local by design - a cast inside a
multi-line block comment would be flagged, which errs on the side the lint should err on.
"""
stripped = line.lstrip()
if stripped.startswith("*") or stripped.startswith("/*"):
return ""
out = []
in_string = False
i, n = 0, len(line)
while i < n:
c = line[i]
if in_string:
if c == "\\" and i + 1 < n:
out.append(" ")
i += 2
continue
if c == '"':
in_string = False
out.append(c)
i += 1
continue
if c == '"':
in_string = True
out.append(c)
i += 1
continue
if c == "/" and i + 1 < n and line[i + 1] in "/*":
break
out.append(c)
i += 1
return "".join(out)


def main():
offenders = []
scanned = 0
for root in SOURCE_ROOTS:
for path in sorted(root.rglob("*.scala")):
scanned += 1
for n, line in enumerate(path.read_text().splitlines(), 1):
m = CAST.search(code_of(line))
if m:
offenders.append((path.relative_to(ROOT), n, m.group(1), line.strip()))

print(f"check_no_blind_commons_casts: {scanned} source file(s) scanned, "
f"{len(offenders)} blind cast(s) to a Commons list")
if offenders:
print("", file=sys.stderr)
for path, n, kind, line in offenders:
print(f" {path}:{n}", file=sys.stderr)
print(f" {line}", file=sys.stderr)
print(f" -> {kind}.toCommonsList(...)", file=sys.stderr)
print("", file=sys.stderr)
print("Each of these casts a provider result to a Commons list without converting it. The "
"cast is erased, so it checks nothing and defers the failure to the first element "
"access or to serialization. Use the companion's toCommonsList instead.",
file=sys.stderr)
return 1
return 0


if __name__ == "__main__":
sys.exit(main())
Loading
Loading