Skip to content

fix: rework project properties access#3424

Open
Anty0 wants to merge 2 commits intomainfrom
jirikuchynka/fix-properties-achitecture
Open

fix: rework project properties access#3424
Anty0 wants to merge 2 commits intomainfrom
jirikuchynka/fix-properties-achitecture

Conversation

@Anty0
Copy link
Collaborator

@Anty0 Anty0 commented Jan 28, 2026

This changeset refactors the entire Tolgee backend to centralize property access through a single TolgeeProperties class, rather than having individual *Properties classes injected directly via Spring's @ConfigurationProperties scanning.

Key changes:

  1. Changed from @ConfigurationPropertiesScan to @EnableConfigurationProperties(TolgeeProperties::class) in Application.kt
  2. Removed @ConfigurationProperties annotations from all sub-property classes
  3. Replaced direct injections of sub-property classes with TolgeeProperties injection + delegated getters
  4. Renamed rateLimitrateLimits and llmPropertiesllm in TolgeeProperties
  5. Updated MtServiceType enum to use lambda-based property getters instead of class references

Summary by CodeRabbit

  • Chores
    • Consolidated many settings under a unified TolgeeProperties container (centralized config access).
    • Renamed keys: tolgee.rate-limit → tolgee.rate-limits; LLM config moved to tolgee.llm.
    • Moved many configuration prefixes into documentation metadata (DocProperty) with no behavior change.
    • Exposed new/updated config options: mailjet.contactListId, plausible.scriptUrl, slack.clientId & clientSecret, RabbitMQ autostart ports/credentials, and an S3 clear helper.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

Consolidates and centralizes configuration by replacing many per-feature constructor-injected property beans with a single TolgeeProperties holder, moves class-level @ConfigurationProperties metadata into @DocProperty(prefix=...)/@NestedConfigurationProperty, updates MT service wiring to use property getters, and renames rateLimitrateLimits and llmPropertiesllm. Tests and DI sites updated accordingly.

Changes

Cohort / File(s) Summary
Properties & annotations
backend/data/src/main/kotlin/io/tolgee/configuration/tolgee/*, .../machineTranslation/*
Replaced @ConfigurationProperties usages with @DocProperty(prefix=...) and added many @NestedConfigurationProperty annotations; small property additions (e.g., MailjetProperties.contactListId, PlausibleProperties.scriptUrl); check metadata changes.
TolgeeProperties core
backend/data/src/main/kotlin/io/tolgee/configuration/tolgee/TolgeeProperties.kt
Renamed rateLimitrateLimits and llmPropertiesllm; added many nested property annotations.
DI consolidation (production code)
examples: backend/api/.../PublicController.kt, .../PasswordResetController.kt, .../ApplicationStopper.kt, .../WebsocketPublisherConfiguration.kt, .../AuthenticationConfig.kt, .../JwtService.kt, .../RateLimitService.kt, .../MtServiceManager.kt, .../MailJetEmailServiceManager.kt, .../PostHogConfiguration.kt, .../Postgres*Runner*.kt, ee/.../Slack*Controller.kt, ee/.../LlmProviderService.kt
Replaced many specific property constructor parameters with TolgeeProperties and added small private getters that delegate to previous sub-properties; inspect constructor signatures and callers.
Machine translation (MT) wiring
backend/data/src/main/kotlin/io/tolgee/constants/MtServiceType.kt, backend/data/src/main/kotlin/io/tolgee/MtServicesConfiguration.kt, backend/data/src/main/kotlin/io/tolgee/service/machineTranslation/MtServiceConfigService.kt, backend/data/src/main/kotlin/io/tolgee/component/machineTranslation/providers/*
MtServiceType now stores propertiesGetter: (TolgeeProperties)->...; MT service resolution now uses propertiesGetter(tolgeeProperties) instead of bean class lookups; providers read config from tolgeeProperties.machineTranslation.*. Review enum change and service map initialization.
Rate-limit lookup updates
backend/api/src/main/kotlin/io/tolgee/api/v2/controllers/batch/V2ExportController.kt, backend/api/src/main/kotlin/io/tolgee/controllers/ExportController.kt
Switched lookups from tolgeeProperties.rateLimit to tolgeeProperties.rateLimits; verify rate-limit behavior and tests.
Batch / job components
backend/data/src/main/kotlin/io/tolgee/batch/*, .../processors/*
Replaced BatchProperties injection with TolgeeProperties (added batchProperties getters); removed unused BatchProperties param from BatchJobChunkExecutionQueue.
Tests & test wiring
backend/app/src/test/**/*, backend/data/src/test/**/*, backend/testing/src/main/kotlin/.../AbstractSpringTest.kt, ee/backend/tests/...
Tests switched to mock/provide TolgeeProperties or use getters delegating to tolgeeProperties; replaced multiple spies/mocks with a single TolgeeProperties spy in test configs—inspect test setups.
Config scanning / enablement
backend/app/src/main/kotlin/io/tolgee/Application.kt, ee/backend/app/src/main/kotlin/io/tolgee/ee/configuration/EeLiquibaseConfiguration.kt
Replaced @ConfigurationPropertiesScan with @EnableConfigurationProperties(TolgeeProperties::class) and added similar enablement for EE properties.

Sequence Diagram(s)

(Skipped — changes are refactor/DI consolidation and do not introduce a new multi-component sequential flow.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • JanCizmar

Poem

🐰
I hopped through fields and settings wide,
Gathered configs that used to hide.
One Tolgee nest now holds them tight—
I twitch my nose and say, “Refactor: right!” 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: rework project properties access' accurately summarizes the main objective of centralizing configuration property access via TolgeeProperties.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@Anty0 Anty0 force-pushed the jirikuchynka/fix-properties-achitecture branch from f872cd3 to 497fcce Compare January 28, 2026 14:47
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
backend/data/src/main/kotlin/io/tolgee/configuration/tolgee/ImportProperties.kt (1)

22-23: Typos in documentation: "foler" → "folder".

The documentation contains two instances of "foler" that should be "folder".

📝 Proposed fix
-        "└─Project # the foler name is the project name\n" +
-        "  └─validation # the foler name is namespace\n" +
+        "└─Project # the folder name is the project name\n" +
+        "  └─validation # the folder name is namespace\n" +
backend/data/src/main/kotlin/io/tolgee/configuration/tolgee/SsoGlobalProperties.kt (1)

69-80: Ensure SSO validation still runs after de-beaning this class.

With @ConfigurationProperties removed, SsoGlobalProperties is likely no longer a Spring bean, so @PostConstruct may never execute. That would silently skip the required-field checks when enabled = true. Please ensure validate() is invoked from a bean lifecycle hook (e.g., TolgeeProperties @PostConstruct) or migrate to Bean Validation (@Validated + @Valid + @field:NotBlank) on the root properties.

backend/data/src/main/kotlin/io/tolgee/configuration/tolgee/GithubAuthenticationProperties.kt (1)

20-25: Update the description to match the actual token endpoint usage.

The property authorizationUrl is used in GithubOAuthDelegate.kt to exchange the authorization code for an access token via restTemplate.postForObject(). The description incorrectly states "URL to the OAuth authorization screen," but should instead describe it as a token endpoint. This matches the actual implementation and aligns with how GoogleAuthenticationProperties.kt describes the same property as "URL to Google /token API endpoint."

Change the description from "URL to the OAuth authorization screen" to "URL to the OAuth token/access token API endpoint. Useful if you want to authenticate against a self-hosted GitHub Enterprise Server."

backend/data/src/test/kotlin/io/tolgee/security/ratelimit/RateLimitServiceTest.kt (1)

62-95: Reset the new tolgeeProperties mock between tests.

tolgeeProperties is stubbed in setupMocks() but never reset; this can leak interactions or stubbing across tests. Add it to resetMocks().

🔧 Suggested fix
-  fun resetMocks() {
-    Mockito.reset(currentDateProvider, rateLimitProperties, userAccount)
-  }
+  fun resetMocks() {
+    Mockito.reset(currentDateProvider, rateLimitProperties, tolgeeProperties, userAccount)
+  }
backend/security/src/test/kotlin/io/tolgee/security/ratelimit/RateLimitInterceptorTest.kt (1)

49-89: Reset the new tolgeeProperties mock between tests.

tolgeeProperties is stubbed in setupMocks() but never reset; add it to resetMocks() to prevent cross‑test leakage.

🔧 Suggested fix
-  fun resetMocks() {
-    Mockito.reset(rateLimitProperties, currentDateProvider, authenticationFacade, userAccount)
-  }
+  fun resetMocks() {
+    Mockito.reset(rateLimitProperties, tolgeeProperties, currentDateProvider, authenticationFacade, userAccount)
+  }
🤖 Fix all issues with AI agents
In
`@backend/app/src/test/kotlin/io/tolgee/api/v2/controllers/batch/BatchJobTestBase.kt`:
- Around line 31-59: The setup() method mutates shared TolgeeProperties (via
tolgeeProperties and machineTranslationProperties) which can leak into other
cached Spring test contexts; fix by capturing the original relevant fields
(e.g., tolgeeProperties.internal.fakeMtProviders and fields under
machineTranslationProperties.google and .aws) before mutating and restore them
in an `@AfterEach` teardown method, or alternatively annotate the test class or
individual tests with `@DirtiesContext` to force a fresh context; ensure
restoration targets the exact properties you change in setup() so no mutated
state persists across tests.
🧹 Nitpick comments (4)
backend/security/src/test/kotlin/io/tolgee/security/authentication/AuthenticationInterceptorTest.kt (1)

44-47: Consider updating resetMocks to include all mocks for consistency.

The @AfterEach resets authenticationFacade and userAccount but omits tolgeeProperties and authenticationProperties. While JUnit 5's default PER_METHOD lifecycle creates fresh instances anyway, keeping the reset list consistent avoids confusion if the lifecycle changes or if someone copies this pattern elsewhere.

Suggested fix
   `@AfterEach`
   fun resetMocks() {
-    Mockito.reset(authenticationFacade, userAccount)
+    Mockito.reset(authenticationFacade, userAccount, tolgeeProperties, authenticationProperties)
   }
backend/data/src/main/kotlin/io/tolgee/configuration/tolgee/RabbitmqAutostartProperties.kt (1)

15-26: Pre-existing documentation error: enum comments reference "postgres" instead of "rabbitmq".

The doc comments in RabbitMqAutostartMode appear to be copy-pasted from a Postgres autostart class. Consider updating them to reference RabbitMQ for clarity.

📝 Suggested fix
   enum class RabbitMqAutostartMode {
     /**
-     * Starts docker container with postgres
+     * Starts docker container with RabbitMQ
      */
     DOCKER,
 
     /**
-     * Expects that postgres is installed in the same container.
-     * So the Postgres is started with Tolgee.
+     * Expects that RabbitMQ is installed in the same container.
+     * So RabbitMQ is started with Tolgee.
      */
     EMBEDDED,
   }
backend/data/src/test/kotlin/io/tolgee/security/authentication/JwtServiceTest.kt (1)

104-107: Consider adding tolgeeProperties to the reset call for consistency.

The tolgeeProperties mock is not included in resetMocks(), while other mocks are. Although the @BeforeEach re-establishes stubs before each test, including it would maintain consistency and prevent potential issues if tests modify the mock's behavior.

Proposed fix
   `@AfterEach`
   fun resetMocks() {
-    Mockito.reset(authenticationProperties, currentDateProvider, userAccountService, authenticationFacade, userAccount)
+    Mockito.reset(tolgeeProperties, authenticationProperties, currentDateProvider, userAccountService, authenticationFacade, userAccount)
   }
backend/data/src/main/kotlin/io/tolgee/component/machineTranslation/providers/DeeplApiService.kt (1)

77-81: Pre-existing: Potential NPE on null authKey.

The !! operator on line 78 will throw an NPE if authKey is null. This is pre-existing behavior not introduced by this PR, but worth noting for future improvement. Consider adding a null check or using the Elvis operator to provide a safer fallback or clearer error message.

Comment on lines 31 to +59
@Autowired
lateinit var machineTranslationProperties: MachineTranslationProperties
lateinit var tolgeeProperties: TolgeeProperties

val machineTranslationProperties: MachineTranslationProperties get() = tolgeeProperties.machineTranslation

@Autowired
lateinit var entityManager: EntityManager

var fakeBefore: Boolean = false

@Autowired
private lateinit var internalProperties: InternalProperties

@Autowired
private lateinit var testDataService: TestDataService

fun setup() {
batchJobOperationQueue.clear()
testData = BatchJobsTestData()

whenever(internalProperties.fakeMtProviders).thenReturn(true)

val googleMock = mock<GoogleMachineTranslationProperties>()
whenever(googleMock.apiKey).thenReturn("mock")
whenever(googleMock.defaultEnabled).thenReturn(true)
whenever(googleMock.defaultPrimary).thenReturn(true)

whenever(machineTranslationProperties.google).thenReturn(googleMock)
// Set properties directly instead of mocking
tolgeeProperties.internal.fakeMtProviders = true

val awsMock = mock<AwsMachineTranslationProperties>()
whenever(awsMock.defaultEnabled).thenReturn(false)
whenever(awsMock.accessKey).thenReturn("mock")
whenever(awsMock.secretKey).thenReturn("mock")
// Configure Google MT
machineTranslationProperties.google.apiKey = "mock"
machineTranslationProperties.google.defaultEnabled = true
machineTranslationProperties.google.defaultPrimary = true

whenever(machineTranslationProperties.aws).thenReturn(awsMock)
// Configure AWS MT
machineTranslationProperties.aws.defaultEnabled = false
machineTranslationProperties.aws.accessKey = "mock"
machineTranslationProperties.aws.secretKey = "mock"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid leaking mutated TolgeeProperties across cached test contexts.

setup() mutates shared config without restoring it. If Spring reuses the context for other tests, this can bleed state and cause flakiness. Consider restoring previous values (or using @DirtiesContext) after each test.

🛠️ Suggested reset helper
 class BatchJobTestBase {
+  private var prevFakeMtProviders: Boolean? = null
+  private var prevGoogleApiKey: String? = null
+  private var prevGoogleDefaultEnabled: Boolean? = null
+  private var prevGoogleDefaultPrimary: Boolean? = null
+  private var prevAwsDefaultEnabled: Boolean? = null
+  private var prevAwsAccessKey: String? = null
+  private var prevAwsSecretKey: String? = null
+
   fun setup() {
     batchJobOperationQueue.clear()
     testData = BatchJobsTestData()
 
-    // Set properties directly instead of mocking
-    tolgeeProperties.internal.fakeMtProviders = true
+    // Set properties directly instead of mocking (capture previous values first)
+    prevFakeMtProviders = tolgeeProperties.internal.fakeMtProviders
+    prevGoogleApiKey = machineTranslationProperties.google.apiKey
+    prevGoogleDefaultEnabled = machineTranslationProperties.google.defaultEnabled
+    prevGoogleDefaultPrimary = machineTranslationProperties.google.defaultPrimary
+    prevAwsDefaultEnabled = machineTranslationProperties.aws.defaultEnabled
+    prevAwsAccessKey = machineTranslationProperties.aws.accessKey
+    prevAwsSecretKey = machineTranslationProperties.aws.secretKey
+
+    tolgeeProperties.internal.fakeMtProviders = true
 
     // Configure Google MT
     machineTranslationProperties.google.apiKey = "mock"
     machineTranslationProperties.google.defaultEnabled = true
     machineTranslationProperties.google.defaultPrimary = true
 
     // Configure AWS MT
     machineTranslationProperties.aws.defaultEnabled = false
     machineTranslationProperties.aws.accessKey = "mock"
     machineTranslationProperties.aws.secretKey = "mock"
   }
+
+  fun cleanup() {
+    prevFakeMtProviders?.let { tolgeeProperties.internal.fakeMtProviders = it }
+    prevGoogleApiKey?.let { machineTranslationProperties.google.apiKey = it }
+    prevGoogleDefaultEnabled?.let { machineTranslationProperties.google.defaultEnabled = it }
+    prevGoogleDefaultPrimary?.let { machineTranslationProperties.google.defaultPrimary = it }
+    prevAwsDefaultEnabled?.let { machineTranslationProperties.aws.defaultEnabled = it }
+    prevAwsAccessKey?.let { machineTranslationProperties.aws.accessKey = it }
+    prevAwsSecretKey?.let { machineTranslationProperties.aws.secretKey = it }
+  }
 }
🤖 Prompt for AI Agents
In
`@backend/app/src/test/kotlin/io/tolgee/api/v2/controllers/batch/BatchJobTestBase.kt`
around lines 31 - 59, The setup() method mutates shared TolgeeProperties (via
tolgeeProperties and machineTranslationProperties) which can leak into other
cached Spring test contexts; fix by capturing the original relevant fields
(e.g., tolgeeProperties.internal.fakeMtProviders and fields under
machineTranslationProperties.google and .aws) before mutating and restore them
in an `@AfterEach` teardown method, or alternatively annotate the test class or
individual tests with `@DirtiesContext` to force a fresh context; ensure
restoration targets the exact properties you change in setup() so no mutated
state persists across tests.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/app/src/test/kotlin/io/tolgee/service/TelemetryServiceTest.kt (1)

37-40: Add TestData cleanup to @AfterEach method.

The reports when enabled test creates TestData via testDataService.saveTestData(testData.root) but the @AfterEach method doesn't clean it up. Following the pattern used in other tests (e.g., SsoOrganizationsTest, KeyUsageReportingTest), add testDataService.cleanTestData(testData.root) to prevent test pollution.

🧹 Nitpick comments (9)
backend/data/src/main/kotlin/io/tolgee/configuration/tolgee/RabbitmqAutostartProperties.kt (1)

15-26: Pre-existing documentation error: Comments reference Postgres instead of RabbitMQ.

The enum documentation incorrectly mentions "postgres" when this is a RabbitMQ configuration class. This is a pre-existing issue but worth fixing while in this file.

📝 Suggested documentation fix
   enum class RabbitMqAutostartMode {
     /**
-     * Starts docker container with postgres
+     * Starts docker container with RabbitMQ
      */
     DOCKER,
 
     /**
-     * Expects that postgres is installed in the same container.
-     * So the Postgres is started with Tolgee.
+     * Expects that RabbitMQ is installed in the same container.
+     * So RabbitMQ is started with Tolgee.
      */
     EMBEDDED,
   }
backend/data/src/main/kotlin/io/tolgee/configuration/tolgee/ContentStorageS3Properties.kt (1)

15-22: Optional: document intended use of clear().
If this is meant for tests or resets only, a short KDoc helps prevent accidental runtime usage.

📝 Suggested KDoc
 data class ContentStorageS3Properties(
   override var bucketName: String? = null,
   override var accessKey: String? = null,
   override var secretKey: String? = null,
   override var endpoint: String? = null,
   override var signingRegion: String? = null,
   override var path: String? = null,
 ) : S3Config {
+  /**
+   * Resets all S3 properties to null (intended for tests or explicit resets).
+   */
   fun clear() {
     bucketName = null
     accessKey = null
     secretKey = null
     endpoint = null
     signingRegion = null
     path = null
   }
 }
backend/data/src/test/kotlin/io/tolgee/security/authentication/JwtServiceTest.kt (1)

104-107: Consider adding tolgeeProperties to resetMocks().

The newly added tolgeeProperties mock is not included in the Mockito.reset() call. While this may not cause issues since the mock's behavior is set up fresh in each @BeforeEach, including it maintains consistency and prevents potential test pollution.

♻️ Suggested fix
   `@AfterEach`
   fun resetMocks() {
-    Mockito.reset(authenticationProperties, currentDateProvider, userAccountService, authenticationFacade, userAccount)
+    Mockito.reset(tolgeeProperties, authenticationProperties, currentDateProvider, userAccountService, authenticationFacade, userAccount)
   }
backend/security/src/test/kotlin/io/tolgee/security/ratelimit/RateLimitInterceptorTest.kt (1)

87-90: Consider adding tolgeeProperties to resetMocks().

Similar to other test files in this PR, the tolgeeProperties mock should be included in the reset call for consistency and to prevent potential test pollution.

♻️ Suggested fix
   `@AfterEach`
   fun resetMocks() {
-    Mockito.reset(rateLimitProperties, currentDateProvider, authenticationFacade, userAccount)
+    Mockito.reset(tolgeeProperties, rateLimitProperties, currentDateProvider, authenticationFacade, userAccount)
   }
backend/app/src/test/kotlin/io/tolgee/config/BatchJobBaseConfiguration.kt (1)

24-28: Prefer explicit instantiation over class-based spy for clarity.

Mockito.spy(TolgeeProperties::class.java) works correctly since TolgeeProperties has a no-arg constructor with default values. However, being explicit about instantiation improves readability and aligns with modern Mockito conventions:

  `@Bean`
  `@Primary`
  fun tolgeeProperties(): TolgeeProperties {
-   return Mockito.spy(TolgeeProperties::class.java)
+   return Mockito.spy(TolgeeProperties())
  }
backend/data/src/main/kotlin/io/tolgee/service/machineTranslation/MtServiceConfigService.kt (1)

3-10: Remove unused imports (lines 4-10).

The imports for AwsMachineTranslationProperties, AzureCognitiveTranslationProperties, BaiduMachineTranslationProperties, DeeplMachineTranslationProperties, GoogleMachineTranslationProperties, LlmProperties, and MachineTranslationServiceProperties are not referenced anywhere in the file and can be safely removed.

backend/security/src/test/kotlin/io/tolgee/security/authentication/AuthenticationInterceptorTest.kt (1)

44-47: Include all mocks in resetMocks() for consistency.

The resetMocks() method omits tolgeeProperties and authenticationProperties from the reset call. While @BeforeEach re-stubs these mocks, including them ensures clean state and prevents subtle test pollution if future tests verify interactions.

♻️ Suggested fix
   `@AfterEach`
   fun resetMocks() {
-    Mockito.reset(authenticationFacade, userAccount)
+    Mockito.reset(authenticationFacade, userAccount, tolgeeProperties, authenticationProperties)
   }
backend/app/src/main/kotlin/io/tolgee/postgresRunners/PostgresRunnerConfiguration.kt (1)

17-22: Consider using a when expression for mode dispatch.

The if-chain can be expressed more idiomatically in Kotlin using a when expression.

♻️ Suggested refactor
-    if (postgresAutostartProperties.mode == PostgresAutostartMode.DOCKER) {
-      return PostgresDockerRunner(tolgeeProperties)
-    }
-    if (postgresAutostartProperties.mode == PostgresAutostartMode.EMBEDDED) {
-      return PostgresEmbeddedRunner(tolgeeProperties)
-    }
-    return null
+    return when (postgresAutostartProperties.mode) {
+      PostgresAutostartMode.DOCKER -> PostgresDockerRunner(tolgeeProperties)
+      PostgresAutostartMode.EMBEDDED -> PostgresEmbeddedRunner(tolgeeProperties)
+      else -> null
+    }
backend/data/src/main/kotlin/io/tolgee/configuration/tolgee/MailjetProperties.kt (1)

7-9: Consider documenting contactListId for config docs.
If config docs are generated from @DocProperty, add a brief description for the new field.

📝 Suggested documentation
 class MailjetProperties {
   var apiKey: String? = null
   var secretKey: String? = null
+  `@DocProperty`(description = "Mailjet contact list ID for default subscriptions.")
   var contactListId: Long? = null
 }

@bdshadow
Copy link
Contributor

please, don't merge it before branching

@bdshadow
Copy link
Contributor

and what's the benefit of it?
For example, now instead of just injecting the aws, you need to have

private val awsMachineTranslationProperties get() = tolgeeProperties.machineTranslation.aws

Before this refactoring it was also possible to inject consolidated tolgeeProperties and access all nested layers like this. So this pr just is just prohibits it, leading to more code and injecting whole bunch of properties when you need only a single one.
Maybe i'm wrong, but i don't see it to be beneficial.

@Anty0
Copy link
Collaborator Author

Anty0 commented Jan 29, 2026

@bdshadow The issue is that we currently have (at least) two instances of each *Properties. I've been burned by this before - you override something in tests through TolgeeProperties, and it doesn't work. You don't know why until you realize the component is accessing the (let's say) AWS properties directly, not through TolgeeProperties, so you need to modify it there, or even worse, both, if different code paths use the property from TolgeeProperties. I wanted to solve this. (This doesn't really affect unit tests, fortunately, as we create the properties there directly, so we create only a single instance of everything. On the contrary, e2e tests can only set properties on the TolgeeProperties, because that's what PropertiesController does. I feel like this is going to bite us.)

Other than this, we currently have two things broken, which I want to fix this way:

  • Rate limits: You may notice that their prefix is set to rate-limits while the property is named rateLimit. On Tuesday, I realized that this means TolgeeProperties are looking for rate-limit, while directly accessing RateLimitProperties will look for rate-limits. The fact that this is even possible is, in my opinion, completely insane. Contrary to most other properties, we access some rate limits through TolgeeProperties, so they don't get set at all (they stay the default).
  • LLM Properties: Same issue. Fortunately, we always access them directly, so there are no configuration issues, but if anyone were to use TolgeeProperties to access them, they would just get default values.

I'm not saying we have to fix it like this, but it felt like the best way to make sure we never run into the same issue again.

I can also just fix the names of the two broken properties and leave it be for now. ^^
(I'll go and do that anyway, as I want it fixed sooner rather than later; this rework can wait after branching.)

Copy link
Contributor

@JanCizmar JanCizmar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. 👍 I would merge it after branching.

@Anty0 Anty0 requested a review from gabrielshanahan January 29, 2026 14:50
@bdshadow
Copy link
Contributor

@Anty0

The fact that this is even possible is, in my opinion, completely insane.

if jakarta validation like @NotNull, @NotEmpty is put on the properties, then it will fail on the start of the app, when thre is such a mistype. So it's easy to fix. And must be fixed like this.

Another thing is about accessing the properties. This seems weird, need to check. But probably this is because fields inside TolgeeProperties are not injected ,they are created by a default constructor. I might be wrong here, but just annotating it with NestedConfigurationProperty and removing initizalization must be enough to solve this problem. (another option is just to declare a constructor of the TolgeeProperties, where all fields will be injected by spring)

@bdshadow bdshadow self-requested a review January 30, 2026 10:36
Copy link
Contributor

@bdshadow bdshadow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll dare to request changes :) Sorry :)
Feel free to ping me

@Anty0
Copy link
Collaborator Author

Anty0 commented Jan 30, 2026

@bdshadow

if jakarta validation like @NotNull, @notempty is put on the properties

You mean they won't have default values anymore? I'm not sure I understand this one. ^^

Another thing is about accessing the properties

Yes, please check and let me know (after branching). If there is a different way we can make sure the *Properties instances won't be created multiple times, I'm all ears ^^

@bdshadow
Copy link
Contributor

bdshadow commented Feb 2, 2026

@Anty0 my comment was based on my previos experience in java and nested static classes. You can how it works here: https://docs.spring.io/spring-boot/3.5/reference/features/external-config.html#features.external-config.typesafe-configuration-properties.validation

In Tolgee there are too many properties. Moving them to nested classes of TolgeeProperties would just explode it and it won't be useful. And it will even bigger change.

Also the default values are set right in the koltin code, and before i had it all in properties files. In that case validation perfectly worked. Here it is different.

So sorry for delaying this pr, but i'm glad i found it all

@bdshadow bdshadow self-requested a review February 2, 2026 15:04
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.

3 participants