Skip to content

Support per-user UI settings (SettingsStore) on the S3 backend #199

Description

@mauripunzueta

Summary

Add a per-user SettingsStore implementation to the S3 storage backend so a HFS_STORAGE_BACKEND=s3 deployment can serve the /_user/settings endpoints instead of returning 501 Not Implemented.

Context

PR #151 introduced an opaque, per-user JSON UI settings store (SettingsStore trait in helios-persistence, GET/PUT/PATCH /_user/settings in helios-rest). It is implemented for SQLite, PostgreSQL, and now MongoDB — every standalone transactional primary backend.

Per review feedback on #151 (@smunini), S3 is also offered as a standalone primary (HFS_STORAGE_BACKEND=s3, CRUD/versioning/history), so an S3-only operator currently hits an (explained) 501 on the settings endpoints. This issue tracks closing that gap. The 501 message now names the supported backends and explicitly excludes S3/Elasticsearch, so the behaviour is documented rather than silent in the meantime.

Why deferred from #151

  • S3's recommended role is Archive, not a hot transactional primary, so the settings use case (frequent small read-modify-write updates from a UI) is a weaker fit.
  • The SettingsStore contract relies on a read-modify-write with a monotonic version for optimistic locking (surfaced as a weak ETag). On SQLite/Postgres this is a SELECT … FOR UPDATE transaction; on MongoDB a version-conditioned update + retry. S3 has no transactions — correctness requires conditional writes (PutObject with If-Match / If-None-Match), whose availability through the backend's object-store client/SDK needs to be verified before implementing.

Work

  • Confirm the S3 backend's object-store client exposes conditional PutObject (If-Match on the object's ETag, If-None-Match: * for create-if-absent).
  • Store one object per user key (kept out of the FHIR resource key space, mirroring how the relational stores keep user_settings separate from resources).
  • Implement get_settings / put_settings / patch_settings with optimistic concurrency mapped onto conditional writes; translate a failed precondition to ConcurrencyError::OptimisticLockFailure.
  • Wire the store into the start_s3 / start_s3_elasticsearch paths in crates/hfs/src/main.rs (via create_app_with_auth_bulk_and_settings).
  • Integration tests mirroring the SQLite/Postgres/MongoDB SettingsStore suites.
  • Update the /_user/settings 501 message to drop S3 from the unsupported list.

Note

Elasticsearch is intentionally out of scope — it is search-only and never a standalone primary, so its 501 is correct.

Follow-up to #151.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions