feat(preference): add org-scoped user preferences support#1369
feat(preference): add org-scoped user preferences support#1369whoAbhishekSah merged 7 commits intomainfrom
Conversation
Regenerate proto files from raystack/proton with scope_type and scope_id fields added to preference messages: - Preference response message - PreferenceRequestBody (for all Create RPCs) - ListCurrentUserPreferencesRequest (for scope filtering) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update ConnectRPC handlers to support scoped preferences: - CreateUserPreferences: pass scope from request body - CreateCurrentUserPreferences: pass scope from request body - ListCurrentUserPreferences: filter by scope from request params - transformPreferenceToPB: include scope fields in response Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…esRequest Update PROTON_COMMIT to 8e929fce087383f6e36736701ed07036ca6e6fa7 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AllowedScopes field to Trait for configuring valid scope types - Add scope validation in Create: scoped traits require scope, global traits reject scope - Add LoadUserPreferences that merges scoped + global DB values + trait defaults - Update ListUserPreferences and ListCurrentUserPreferences handlers to use LoadUserPreferences - Add ErrInvalidScope error for scope validation failures - Add unit tests for LoadUserPreferences Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds scope-aware user preferences: trait-level allowed scopes and validation, a Service.LoadUserPreferences method that merges scoped/global prefs with trait defaults, exposes scope fields and query params in API/protobufs, updates interfaces/mocks/tests, and enforces authorization for current-user preference endpoints. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant APIHandler as API Handler
participant Auth as Authorization Interceptor
participant Service as Preference Service
participant Repo as Repository
Client->>APIHandler: GET /users/{id}/preferences?scope_type=org&scope_id=xyz
APIHandler->>Auth: pass request (interceptor)
Auth->>Auth: validate caller (if org scope → GetPermission)
Auth-->>APIHandler: allow / deny
APIHandler->>Service: LoadUserPreferences(ctx, filter{UserID, ScopeType, ScopeID})
Service->>Repo: List global preferences (user)
Repo-->>Service: global prefs
Service->>Repo: List scoped preferences (scope_type/org, scope_id/xyz)
Repo-->>Service: scoped prefs
Service->>Service: Merge (scoped > global), apply trait defaults
Service-->>APIHandler: merged preferences with scope metadata
APIHandler-->>Client: respond with preferences (includes ScopeType/ScopeID)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@internal/api/v1beta1connect/preferences.go`:
- Around line 155-162: Update the misleading comments that describe
LoadUserPreferences behavior: change the comment blocks around the handler that
call h.preferenceService.LoadUserPreferences to state that trait defaults are
always merged into the returned preference set. Specify the two cases as: "With
scope: returns scoped DB values (highest priority) + global DB values + trait
defaults" and "Without scope: returns global DB values + trait defaults", and
note that only the DB precedence changes with scope while defaults are always
applied; update any related comment near preferenceService.LoadUserPreferences /
LoadUserPreferences to match this wording.
In `@proto/apidocs.swagger.yaml`:
- Around line 10991-11006: Update the scope_type and scope_id parameter
descriptions to reflect the actual merge priority and global fallback: state
that when scope_type is omitted the API returns global + all scoped preferences;
when scope_type is set without scope_id it returns preferences for that context
merged with global defaults; when both scope_type and scope_id are set it
returns preferences for that specific scope merged with global preferences and
trait defaults for any unset keys (i.e., specific scope > scoped defaults >
global fallback). Ensure the changes reference the existing parameter names
scope_type and scope_id in the descriptions.
- Around line 14436-14445: The OpenAPI docs for scope_type and scope_id lack
guidance about validation rules for request bodies; update the documentation for
the request schemas that use scope_type and scope_id (referencing the scope_type
and scope_id properties) to explicitly state that: scoped traits require both
scope_type and scope_id to be present, global-only traits must omit both
scope_type and scope_id, and mixed/invalid combinations will be rejected by
server validation; add these rules to the relevant request body descriptions and
examples so clients know when to include or omit scope fields.
🧹 Nitpick comments (3)
proto/v1beta1/frontier.pb.validate.go (1)
43485-43488: Add request-level validation for scope inputs (if expected).Both validators currently do nothing for
scope_type/scope_id. If the API expects “both set or both empty” (and/or restricts to known scope types), encode that in the proto validation rules and regenerate instead of relying on downstream checks.Also applies to: 44018-44021
internal/api/v1beta1connect/preferences_test.go (1)
631-633: Consider adding scope-aware test cases.The existing tests only exercise
LoadUserPreferenceswithout scope parameters (testing global preference loading). Given that the PR adds organization-scoped preferences as a key feature, consider adding test cases that passScopeTypeandScopeIDin the request to verify the handler correctly propagates scope information to the service layer.Also applies to: 646-648, 659-683
core/preference/service.go (1)
146-150: Non-deterministic ordering for orphan preferences.When iterating over
prefMapto add remaining preferences (those without matching traits), Go map iteration order is non-deterministic. This means the order of orphan preferences in the result could vary between calls.While this is unlikely to cause issues in practice (orphan preferences "shouldn't happen normally" as the comment notes), consider if stable ordering is important for API responses or testing.
Pull Request Test Coverage Report for Build 21744337062Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
Update comments to accurately reflect that preferences are always merged with trait defaults, not just when scope is provided. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When CreateCurrentUserPreferences or ListCurrentUserPreferences is called with scope_type=app/organization, verify the user has get permission on the organization. Other scope types are validated by the handler against the trait's AllowedScopes configuration. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
Adds support for org-scoped user preferences, allowing users to have different preference values per organization context.
Changes
allowed_scopesfield toTraitconfig to specify valid scope types (e.g.,["app/organization"])LoadUserPreferencesservice method that merges preferences with priorityListUserPreferencesandListCurrentUserPreferenceshandlers to return complete preference setsListUserPreferencesRequestgetpermission on the organization whenscope_type=app/organizationHow It Works
Trait Configuration
Traits can be configured as global-only or scoped:
Create Preference Validation
allowed_scopes)allowed_scopes)allowed_scopes: [...])allowed_scopes: [...])Authorization
When
CreateCurrentUserPreferencesorListCurrentUserPreferencesis called with an org scope:getpermission on the organizationAllowedScopesList Preferences Response
When listing preferences, the response merges values with priority:
Example: User has
newsletter=trueglobally,theme=darkfor org-123:Test Plan
LoadUserPreferencescovering:Related
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
API Changes
scope_typeandscope_idquery parameters to preference endpoints for scoped filtering.Security