feat: add user flag implementation#1722
Conversation
| ) | ||
| with db.start_db_session() as session: | ||
| kwargs["db_session"] = session | ||
| if inspect.iscoroutinefunction(func): |
There was a problem hiding this comment.
Pre-existing bug: the sync wrapper in with_users_db_session returns a coroutine without awaiting it, so the session commits an empty transaction before FastAPI runs the endpoint. Writes never persisted. Fixed here because this PR introduces the first write endpoints that hit this decorator.
e2253ae to
fdea16e
Compare
Additional Test Cases
Operations API — Feature Flag CRUD6. Create a new feature flag curl -s -X POST http://localhost:8081/v1/operations/feature-flags \
-H "Content-Type: application/json" \
-d '{"id":"new_feature","name":"New Feature","value_type":"boolean","default_value":false}' | jq .Expected: 7. Reject duplicate flag ID curl -s -o /dev/null -w "%{http_code}" -X POST http://localhost:8081/v1/operations/feature-flags \
-H "Content-Type: application/json" \
-d '{"id":"beta_editor","value_type":"boolean","default_value":false}'Expected: 8. Reject mismatched curl -s -o /dev/null -w "%{http_code}" -X POST http://localhost:8081/v1/operations/feature-flags \
-H "Content-Type: application/json" \
-d '{"id":"bad_flag","value_type":"numeric","default_value":"not-a-number"}'Expected: 9. Update a flag's name and default value curl -s -X PUT http://localhost:8081/v1/operations/feature-flags/new_feature \
-H "Content-Type: application/json" \
-d '{"name":"Renamed Feature","default_value":true}' | jq '{id,name,default_value}'Expected: 10. Reject update when curl -s -o /dev/null -w "%{http_code}" -X PUT http://localhost:8081/v1/operations/feature-flags/new_feature \
-H "Content-Type: application/json" \
-d '{"default_value":"wrong-type"}'Expected: 11. Delete a flag curl -s -o /dev/null -w "%{http_code}" -X DELETE \
http://localhost:8081/v1/operations/feature-flags/new_featureExpected: 12. Delete a non-existent flag curl -s -o /dev/null -w "%{http_code}" -X DELETE \
http://localhost:8081/v1/operations/feature-flags/does_not_existExpected: Operations API — User / Flag Assignment Edge Cases13. Get a non-existent user curl -s -o /dev/null -w "%{http_code}" \
http://localhost:8081/v1/operations/users/ghost_userExpected: 14. Assign a non-existent flag to a user curl -s -X PATCH \
http://localhost:8081/v1/operations/users/test_user_bob_00000000000002/feature-flags \
-H "Content-Type: application/json" \
-d '{"assignments":[{"feature_flag_id":"no_such_flag","value":true}]}' | jq .Expected: 15. Clear all overrides for a user (empty assignments) curl -s -X PATCH \
http://localhost:8081/v1/operations/users/test_user_carol_000000000003/feature-flags \
-H "Content-Type: application/json" \
-d '{"assignments":[]}' | jq '{id, features}'Expected: 16. Assignments are replace-not-append (idempotency) # Assign only max_results; any previous overrides (e.g. beta_editor) must be gone
curl -s -X PATCH \
http://localhost:8081/v1/operations/users/test_user_bob_00000000000002/feature-flags \
-H "Content-Type: application/json" \
-d '{"assignments":[{"feature_flag_id":"max_results","value":200}]}' | jq '{id, features}'Expected: only 17. List users filtered by search query curl -s "http://localhost:8081/v1/operations/users?search_query=bob" | jq '.[].email'Expected: only Bob's email is returned. User Service —
|
| type: boolean | ||
| description: Whether the user has opted in to receive API announcement emails. | ||
| default: false | ||
| features: |
There was a problem hiding this comment.
A feature flag is a term used to denote true/false values. Here we have more options, not just boolean values.
| "404": | ||
| description: User not found. | ||
| /v1/operations/users/{user_id}/feature-flags: | ||
| patch: |
There was a problem hiding this comment.
It's a detail, but since it replaces everything, maybe a PUT would be more appropriate?
| raise HTTPException( | ||
| status_code=404, | ||
| detail=f"Feature flag(s) not found: {', '.join(sorted(missing))}", | ||
| ) |
There was a problem hiding this comment.
Apparently it should also check that the changed flag corresponds to the immutable value type.
jcpitre
left a comment
There was a problem hiding this comment.
I added a couple of minor comments.
| description: 1Password reference for users DB app password (e.g. op://vault/item/password) | ||
| required: true | ||
| type: string | ||
| POSTGRE_DEV_USER_APP_NAME_1PASSWORD: |
There was a problem hiding this comment.
DEV shares the QA instance.
| status_code=500, detail=f"Internal server error: {str(e)}" | ||
| ) | ||
|
|
||
| async def get_feeds( |
There was a problem hiding this comment.
This was needed because the previous PR downgraded the open api generator
| type: boolean | ||
| description: Whether the user has opted in to receive API announcement emails. | ||
| default: false | ||
| features: |
There was a problem hiding this comment.
A feature flag is a term used to denote true/false values. Here we have more options, not just boolean values.
| "404": | ||
| description: User not found. | ||
| /v1/operations/users/{user_id}/feature-flags: | ||
| patch: |
| raise HTTPException( | ||
| status_code=404, | ||
| detail=f"Feature flag(s) not found: {', '.join(sorted(missing))}", | ||
| ) |
Summary:
This PR adds the necessary endpoints to support user-level feature flags. It also adds endpoints to the operations API supporting assigning and removing feature flags.
Expected behavior:
Testing tips:
Testing Locally
Prerequisites
1. List feature flags
Expected: 5 flags (
beta_editor,max_results,allowed_formats,ui_theme,extra_config) each withvalue_typeanddefault_value.2. Get a user's flag state (admin view)
Expected: features show both
default_valueanduser_valueside by side.3. Assign flags to a user
Expected: response shows Carol with
user_value: trueforbeta_editor. Re-querying the user confirms it persisted.4. Verify resolved value (user-facing view)
Expected: each flag shows a single resolved
value(user override if set, otherwise the flag's default). No wrapper object, nodefault_valueexposed.5. Run unit tests
From our AI friend
This pull request introduces feature flag support to the user service, enabling user profiles to include feature flag information. It updates the user data model, API implementation, and relevant tests to handle feature flags, and improves database session handling for both synchronous and asynchronous functions. The changes also include some configuration and documentation updates.
Feature flag support in user profiles:
UserProfilemodel now includes afeaturesfield, populated from the user's associated feature flags in the database. Thefrom_ormmethod inAppUserImplis updated to map feature flags from the ORM model to the API model, including default and user-specific values.UsersApiImpl) is updated to eagerly load user feature flags and their definitions when fetching or updating users, ensuring thefeaturesfield is correctly populated. [1] [2]Testing improvements for feature flags:
Database session decorator enhancements:
with_users_db_sessiondecorator is improved to support both synchronous and asynchronous functions, ensuring correct database session management regardless of function type. [1] [2] [3]Documentation and configuration updates:
.env.localconfiguration adds aLOCAL_ENVvariable.Please make sure these boxes are checked before submitting your pull request - thanks!
./scripts/api-tests.shto make sure you didn't break anything