Repository navigation
chore: let settings read their own value from the settings store - #2339
Conversation
When we introduced the per-setting beans in #2257 the idea was to have the settings open for extension and closed for modification, so adding a setting never touches `SettingsService`. We deprecated `isReadOnly()` on the service back then, but the max extension size that came in with #2287 ended up doing exactly the opposite and added `getMaxExtensionSize()` to the service, so we had one getter deprecated and another one alive, and both settings reading the cache from the same bean that encodes and validates them. This settles that. A setting bean only knows how to encode and decode its rows, and a setting the server needs at runtime gets its own reader (`ReadOnlyMode`, `ExtensionSizeLimitService`) that asks the service for the current value through a package-private `current(setting)`. Everything else injects the reader, the setting beans, their keys and the cache are package-private now, and the getters on the service are gone. We're also taking the json mapping out of the service. It returns `SettingRows` now and `SettingsJsonMapper`, next to the beans since it's the only place that can see the keys, does the mapping for `AdminAPI` and `SettingsAPI`, so the DTOs don't name any setting codec anymore. Assisted-By: anthropic:claude-fable-5
39f57c5 to
4f67eae
Compare
There was a problem hiding this comment.
🟢 Approval recommended
Static review supports a behavior-preserving refactor with updated tests and no blocking defects.
1 open finding
What changed in this PR
Refactors server settings access into dedicated readers, keeping SettingsService independent of individual settings and JSON payloads.
Changes:
- Migrates runtime consumers to per-setting readers and hides setting implementations.
- Moves JSON conversion into
SettingsJsonMapper. - Updates affected tests and documents the settings architecture.
| File | Description |
|---|---|
| server/src/test/java/org/eclipse/openvsx/settings/SettingsServiceTest.java | Tests row-based service operations. |
| server/src/test/java/org/eclipse/openvsx/settings/SettingsJsonMapperTest.java | Adds mapping coverage. |
| server/src/test/java/org/eclipse/openvsx/settings/SettingsAPITest.java | Exercises endpoint mapping. |
| server/src/test/java/org/eclipse/openvsx/settings/ReadOnlySettingTest.java | Removes obsolete accessor coverage. |
| server/src/test/java/org/eclipse/openvsx/settings/ReadOnlyEndpointAspectTest.java | Uses the reader provider. |
| server/src/test/java/org/eclipse/openvsx/settings/ExtensionSizeLimitServiceTest.java | Updates default-limit stubbing. |
| server/src/test/java/org/eclipse/openvsx/migration/MigrationItemJobRequestHandlerTest.java | Mocks the read-only reader. |
| server/src/test/java/org/eclipse/openvsx/json/SiteSettingsJsonTest.java | Keeps payload serialization coverage. |
| server/src/test/java/org/eclipse/openvsx/json/SettingsJsonTest.java | Moves mapping tests out. |
| server/src/test/java/org/eclipse/openvsx/extension_control/ExtensionControlJobRequestHandlerTest.java | Updates reader injection. |
| server/src/test/java/org/eclipse/openvsx/analytics/ingestion/DownloadIngestionRunnerTest.java | Updates read-only stubbing. |
| server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java | Uses row responses and real mapping. |
| server/src/main/java/org/eclipse/openvsx/settings/WritableSetting.java | Documents codec responsibilities. |
| server/src/main/java/org/eclipse/openvsx/settings/SettingsService.java | Exposes rows and generic setting reads. |
| server/src/main/java/org/eclipse/openvsx/settings/SettingsJsonMapper.java | Centralizes JSON conversion. |
| server/src/main/java/org/eclipse/openvsx/settings/SettingsCache.java | Makes the cache package-private. |
| server/src/main/java/org/eclipse/openvsx/settings/SettingsAPI.java | Maps published rows to JSON. |
| server/src/main/java/org/eclipse/openvsx/settings/ReadOnlySetting.java | Removes cache access and public visibility. |
| server/src/main/java/org/eclipse/openvsx/settings/ReadOnlyMode.java | Adds the runtime flag reader. |
| server/src/main/java/org/eclipse/openvsx/settings/ReadOnlyEndpointAspect.java | Resolves the reader lazily. |
| server/src/main/java/org/eclipse/openvsx/settings/package-info.java | Documents settings boundaries. |
| server/src/main/java/org/eclipse/openvsx/settings/MaxExtensionSizeSetting.java | Separates encoding from runtime access. |
| server/src/main/java/org/eclipse/openvsx/settings/ExtensionSizeLimitService.java | Reads the default through the service. |
| server/src/main/java/org/eclipse/openvsx/settings/BannerSetting.java | Hides implementation and keys. |
| server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CollectUsageStatsHandler.java | Uses the read-only reader. |
| server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CalculateDailyUsageStatsHandler.java | Uses the read-only reader. |
| server/src/main/java/org/eclipse/openvsx/publish/PublishingConfig.java | Updates a method name in documentation. |
| server/src/main/java/org/eclipse/openvsx/migration/MigrationItemJobRequestHandler.java | Uses the read-only reader. |
| server/src/main/java/org/eclipse/openvsx/json/SiteSettingsJson.java | Removes row conversion. |
| server/src/main/java/org/eclipse/openvsx/json/SettingsJson.java | Removes setting-specific mapping. |
| server/src/main/java/org/eclipse/openvsx/extension_control/ExtensionControlJobRequestHandler.java | Uses the read-only reader. |
| server/src/main/java/org/eclipse/openvsx/analytics/ingestion/DownloadIngestionRunner.java | Migrates ingestion read-only checks. |
| server/src/main/java/org/eclipse/openvsx/admin/MonthlyAdminStatisticsJobRequestHandler.java | Uses the read-only reader. |
| server/src/main/java/org/eclipse/openvsx/admin/AdminAPI.java | Maps settings requests and responses. |
| server/src/main/java/org/eclipse/openvsx/adapter/VSCodeIdDailyUpdateJobRequestHandler.java | Uses the read-only reader. |
| server/src/main/java/org/eclipse/openvsx/accesstoken/UpgradePersonalAccessTokenHandler.java | Uses the read-only reader. |
| server/src/main/java/org/eclipse/openvsx/accesstoken/NotifyPersonalAccessTokenExpirationHandler.java | Uses the read-only reader. |
| server/src/main/java/org/eclipse/openvsx/accesstoken/LegacyPersonalAccessTokenExpirationHandler.java | Uses the read-only reader. |
| server/src/main/java/org/eclipse/openvsx/accesstoken/ExpirePersonalAccessTokensHandler.java | Uses the read-only reader. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
I am not convinced the separate reader components ( What the split gains: What it costs:
Suggestion: let the setting bean read its own value, as Plain getters on Minor, on the hot path: |
A setting the server consults exposes its own accessor over the cached snapshot, so the reader beans go, SettingsCache becomes the public SettingsStore, and each consulted setting moves into a package of its own.
The previous commit captured only the file moves. This is the change itself: ReadOnlySetting.isEnabled() and MaxExtensionSizeSetting.getValue() over the public SettingsStore, the reader beans and SettingsService.current removed, callers injecting the setting, and the tests using the key constants.
@netomi fair enough, I agree the readers were not paying for themselves. The real problem was the package-private A setting the server consults now reads its own value over the cached snapshot ( #2340 follows the same shape with |
netomi
left a comment
There was a problem hiding this comment.
This looks much better ty.
The sub packages do not really make sense imho, we can introduce that when we have a lot of settings, right now this feels like an overkill, also its inconsistent as some settings do not have a subpackage.
I will revert that and update the readonly aspect, it misses some API classes afaict.
Move the settings out of the readonly and extensionsize subpackages back into settings, which shrinks the change and lets SettingsStore stay package-private. ReadOnlyEndpointAspect only matched RegistryAPI, UserAPI and the admin package, so the TrustedPublishingAPI mutations ran in read-only mode. Match every class named *API instead, and add a test that every @MutatingOperation is guarded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

When we introduced the per-setting beans in #2257 the idea was to have the settings open for extension and closed for modification, so adding a setting never touches
SettingsService. We deprecatedisReadOnly()on the service back then, but the max extension size that came in with #2287 ended up doing exactly the opposite and addedgetMaxExtensionSize()to the service, so we had one getter deprecated and another one alive.This settles that the way the deprecation already pointed: a setting the server consults exposes its own accessor (
ReadOnlySetting.isEnabled(),MaxExtensionSizeSetting.getValue()) over the cached snapshot, and callers inject the setting. The getters on the service are gone and the service keeps knowing nothing about individual settings, it still only takes the list.SettingsCacheis nowSettingsStore: the store as this node sees it. It stays package-private, since every setting lives in thesettingspackage. A setting'sread/merge/validate/toRowsstill only see the rows they are handed, so a save reads the store exactly once. Tests outside the package use the key constants instead of spelling the keys.We're also taking the json mapping out of the service. It returns
SettingRowsnow andSettingsJsonMapperdoes the mapping forAdminAPIandSettingsAPI, so the DTOs don't name any setting codec anymore.ReadOnlyEndpointAspectresolves the setting once through aSingletonSupplierinstead of a bean lookup on every guarded request.Fix: read-only mode did not cover trusted publishing
The aspect's class filter only admitted
RegistryAPI,UserAPIand the*APIclasses of theadminpackage, so the three@MutatingOperationendpoints ofTrustedPublishingAPI(create and delete a trusted publisher, request a publish token) kept working while the registry was read-only. The filter now admits every class named*API, andmatchesEveryMutatingOperationInTheRegistryscans the classpath and fails for any@MutatingOperationthe aspect does not guard. Added a changelog entry.