Skip to content

[PHEE-417] Bind configuration through typed records - #4

Merged
tdaly61 merged 1 commit into
openMF:devfrom
GiulioRinalduzzi:refactor/phee-417-auth-config
Oct 7, 2026
Merged

tdaly61 merged 1 commit into
openMF:devfrom
GiulioRinalduzzi:refactor/phee-417-auth-config

Conversation

@GiulioRinalduzzi

Copy link
Copy Markdown
Contributor

Eighteen of the twenty-one @Value fields, reading ten keys in two classes, become two typed records in org.apache.fineract.config.properties. The configuration this service needs is now written down in one place, and a missing or wrong value stops startup with a message that names the property. Three @Value stay, each for a reason given below.

What changes

Record Prefix Keys Read by
FineractDatasourceProperties fineract.datasource core.host, core.port, core.schema, core.username, core.password, common.protocol, common.subprotocol, common.driverclass_name DataSourcePerTenantService, TenantDatabaseUpgradeService
TokenProperties token access.validity-seconds, refresh.validity-seconds TenantDatabaseUpgradeService

Every record is @Validated, every component is @NotNull, and the database port is an Integer, because @NotNull cannot fire on a primitive. No record has a @DefaultValue: none of the eighteen replaced @Value had a default. FineractDatasourceProperties has the same shape as the record of the same name in paymenthub-ee-bff.

The two token validities stay strings, as they were: TenantDatabaseUpgradeService passes them on as text to the tenant schema migrations.

The two services take the records through their constructor. No property name changes, and nothing about tokens, users or tenants changes.

spring-boot-starter-validation is added, with the version from the BOM. Nothing on the classpath provided Bean Validation, and without it @Validated does nothing.

Three @Value that stay

  • spring.security.filter.order in ServerApplication. This is Spring Boot's own key, and Spring Boot gives it a default of -100. With that default, Spring Security runs before the tenant filter, so the token audience check runs with no tenant and every valid token is refused with 401 (see the comment on this key in application.properties). A bare @Value keeps the key required, so a configuration without it fails at startup instead of running with -100.
  • fineract.datasource.core.auto-update, read as @Value("${fineract.datasource.core.auto-update:true}"). A missing key means true, but an empty one stops startup. A record cannot do both: an empty value binds to null, and a default would then turn it into true.
  • tenants, a Spring expression (#{'${tenants}'.split(',')}), which only @Value evaluates.

Same result as before

  • A missing key stops startup, as before, and the error names the property.
  • An empty number stops startup, as before (see the pods below).
  • An empty String is accepted, as before.

How it was checked

Build. ./gradlew clean build with JDK 21.0.10: green, 28 tests (5 new, plus the 23 in PemUtilsTest, ResourceServerConfigValidatorTest and TokenControllerTest). This repo has no spotless, checkstyle or Error Prone.

The new test. ShippedConfigBindsTest binds the two records from the real application.properties and checks the values, including driverclass_name, which binds to driverclassName. It checks that each record, on its own and with nothing configured, refuses to start and names its prefix. It checks that a key missing inside a nested group (core.password) stops startup, that an empty core.port stops startup, and that an empty token.access.validity-seconds is accepted. To check that the test can fail, it was run against three broken copies of the code, one at a time:

Broken copy Tests that fail
no @Validated on TokenProperties everyRecordRefusesToStartWhenItsSectionIsMissing
no @Valid on core aMissingKeyInsideAGroupStopsStartup, aValueSetToNothingOnANumberFieldStopsStartup
no @NotNull on port aValueSetToNothingOnANumberFieldStopsStartup

What a deployment passes. This service is not deployed on gazelle3, and there is no CR for it in GAZ-348, so no deployment environment exists to compare with. Its configuration comes from application.properties and the bb, med and large profiles. None of them uses a ${...} placeholder.

Runtime, against a throwaway MySQL. The service applies database migrations at startup, so it was not pointed at any shared database. A separate MySQL 5.7 pod was started on gazelle3 with temporary storage and empty schemas, and then deleted. The service pods ran with profile large (tenants tn01, tn02) and with only the database host and password changed to reach that MySQL. Every pod was deleted afterwards.

Jar Change Result
this branch none applied 1 migration to tenants and 28 to each of tn01 and tn02, then started in 19.9s, no error
dev none, same database schemas up to date, started in 6.8s, no error
this branch FINERACT_DATASOURCE_CORE_PORT="" did not start: Property: fineract.datasource.core.port, Reason: must not be null, exit 1
dev FINERACT_DATASOURCE_CORE_PORT="" did not start: NumberFormatException: For input string: "", exit 1

The HTTP checks done for the migration (token grants, /api/v1, tenant isolation) were not repeated here. This PR changes how the database settings and token validities are read, not how they are used.

Ticket: PHEE-417.

Eighteen of the twenty-one @value fields, reading ten keys in two
classes, become two records in org.apache.fineract.config.properties:
fineract.datasource and token. DataSourcePerTenantService and
TenantDatabaseUpgradeService now take them through their constructor.

Every record is @validated, every component is @NotNull, and the
database port is an Integer. None has a @DefaultValue: none of the
eighteen replaced @value had a default. A missing key stops startup,
as before, and the error names the property. An empty number stops
startup, an empty String is accepted.

The two token validities stay strings, as they were: they are passed
on as text to the tenant schema migrations.

spring-boot-starter-validation is added because nothing on the
classpath provided Bean Validation; without it @validated is ignored.
The version comes from the BOM.

Three @value stay:
- spring.security.filter.order. It is Spring Boot's own key, and
  Spring Boot gives it a default of -100. A bare @value keeps it
  required, so a configuration without it fails at startup instead of
  silently running the security filter before the tenant filter.
- fineract.datasource.core.auto-update, read with a default of true. A
  missing key means true, but an empty one stops startup. In a record
  an empty value binds to null and a default turns it into true.
- tenants, a Spring expression, which only @value evaluates.

ShippedConfigBindsTest binds the two records from the real
application.properties, checks that each one on its own refuses to
start when nothing is configured, that a key missing inside a nested
group stops startup, and pins the empty values: an empty number stops
startup, an empty String is accepted.
@GiulioRinalduzzi
GiulioRinalduzzi requested a review from a team October 6, 2026 16:53
@tdaly61
tdaly61 merged commit 75a9dae into openMF:dev Oct 7, 2026
1 check passed
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.

2 participants