Skip to content

feat(__future__): Add MV capabilities to the experimental update-flag endpoints#7955

Draft
emyller wants to merge 7 commits into
mainfrom
feat/multivariate-update-flag-endpoints
Draft

feat(__future__): Add MV capabilities to the experimental update-flag endpoints#7955
emyller wants to merge 7 commits into
mainfrom
feat/multivariate-update-flag-endpoints

Conversation

@emyller

@emyller emyller commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Thanks for submitting a PR! Please check the boxes below:

  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Timeline for context

  1. New — and experimental — update-flag endpoints were introduced as Options A and B (feat(api): add experimental feature flag update endpoints  #6305).
  2. The experimentation team has eventually made use of the experimental endpoints, and implemented support to, specifically, re-weighting segment override multivariate flags in the Option B endpoint (feat(experimentation): experiment rollout with multivariate segment override #7851).
  3. This PR adds full MV support to both endpoints.

Changes

Contributes to #7642

  • Introduces full MV (multi variate) support for both experimental update-flags endpoints.
  • Behaviour is consistent to UX in the dashboard.
  • Makes all keys except feature in the update-flag schema optional, to allow for isolated changes to MVs — and other properties too, why not.
  • Sorry it adds review effort, but helps sanity: disambiguation of v1/v2 used in both experimental update-flags and feature versioning; standardised to Option A/B taxonomy already adopted by docs. Plus strict-typing of all interfaces touched in the diff.

How did you test this code?

Unit tests.

How to review

Reading new docs should help: https://docs-lpoi8k37l-flagsmith.vercel.app/integrating-with-flagsmith/flagsmith-api-overview/admin-api/updating-flags

Reading commit-by-commit helps a ton.

3/5 effort.

@emyller emyller self-assigned this Jul 6, 2026
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jul 14, 2026 6:20pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
flagsmith-frontend-preview Ignored Ignored Preview Jul 14, 2026 6:20pm
flagsmith-frontend-staging Ignored Ignored Preview Jul 14, 2026 6:20pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request renames the update-flag endpoints, serialisers, and services to Option A and Option B. It adds typed payload schemas and change-set dataclasses for feature values, multivariate options, and segment overrides. The versioning service is refactored to use shared helpers for feature-state and multivariate updates. Rollout code, routing, views, tests, OpenAPI schemas, and documentation are updated throughout.

Estimated code review effort: 4 (Complex) | ~60 minutes


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

@github-actions github-actions Bot added api Issue related to the REST API docs Documentation updates feature New feature or request and removed docs Documentation updates labels Jul 6, 2026
@emyller
emyller force-pushed the feat/multivariate-update-flag-endpoints branch from 420e419 to d64e257 Compare July 7, 2026 01:00
@github-actions github-actions Bot added docs Documentation updates feature New feature or request and removed feature New feature or request docs Documentation updates labels Jul 7, 2026
Comment thread api/tests/integration/conftest.py
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-frontend:pr-7955 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-api-test:pr-7955 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-7955 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith:pr-7955 Finished ✅ Results

@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 8, 2026
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  6 passed

Details

stats  6 tests across 4 suites
duration  40.7 seconds
commit  8c25092
info  🔄 Run: #18118 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  41.5 seconds
commit  8c25092
info  🔄 Run: #18118 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  38 seconds
commit  8c25092
info  🔄 Run: #18118 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  7 passed

Details

stats  7 tests across 6 suites
duration  35 seconds
commit  8c25092
info  🔄 Run: #18118 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  42.8 seconds
commit  9d8dc5c
info  🔄 Run: #18142 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  45.4 seconds
commit  9d8dc5c
info  🔄 Run: #18142 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  5 passed

Details

stats  5 tests across 4 suites
duration  34.1 seconds
commit  9d8dc5c
info  🔄 Run: #18142 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  4 passed

Details

stats  4 tests across 2 suites
duration  11.7 seconds
commit  9d8dc5c
info  🔄 Run: #18142 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  34.3 seconds
commit  42ea717
info  🔄 Run: #18170 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  36.7 seconds
commit  42ea717
info  🔄 Run: #18170 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  37.7 seconds
commit  42ea717
info  🔄 Run: #18170 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  53.9 seconds
commit  42ea717
info  🔄 Run: #18170 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  5 passed

Details

stats  5 tests across 3 suites
duration  12.1 seconds
commit  656a5ca
info  🔄 Run: #18171 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  41 seconds
commit  656a5ca
info  🔄 Run: #18171 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  6 passed

Details

stats  6 tests across 4 suites
duration  12.1 seconds
commit  656a5ca
info  🔄 Run: #18171 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  59.5 seconds
commit  656a5ca
info  🔄 Run: #18171 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  40.8 seconds
commit  7141260
info  🔄 Run: #18201 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  43.3 seconds
commit  7141260
info  🔄 Run: #18201 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  4 passed

Details

stats  4 tests across 4 suites
duration  41.9 seconds
commit  7141260
info  🔄 Run: #18201 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  5 passed

Details

stats  5 tests across 4 suites
duration  1 minute, 3 seconds
commit  7141260
info  🔄 Run: #18201 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  4 passed

Details

stats  4 tests across 4 suites
duration  38.7 seconds
commit  e75e61c
info  🔄 Run: #18203 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  5 passed

Details

stats  5 tests across 3 suites
duration  20 seconds
commit  e75e61c
info  🔄 Run: #18203 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  6 passed

Details

stats  6 tests across 4 suites
duration  18 seconds
commit  e75e61c
info  🔄 Run: #18203 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  7 passed

Details

stats  7 tests across 5 suites
duration  43.3 seconds
commit  e75e61c
info  🔄 Run: #18203 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  18 seconds
commit  2c3f224
info  🔄 Run: #18254 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  44.9 seconds
commit  2c3f224
info  🔄 Run: #18254 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  6 passed

Details

stats  6 tests across 4 suites
duration  29.6 seconds
commit  2c3f224
info  🔄 Run: #18254 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  5 passed

Details

stats  5 tests across 4 suites
duration  60 seconds
commit  2c3f224
info  🔄 Run: #18254 (attempt 1)

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Visual Regression

19 screenshots compared. See report for details.
View full report

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
api/features/feature_states/serializers.py (1)

238-254: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

N+1 query in validate_segment_overrides.

One Segment.objects.filter(...).exists() query per segment override via validate_segment_id. Already flagged with a TODO — worth batching into a single filter(id__in=segment_ids, project_id=...).count() check once out of experimentation, as noted.

♻️ Possible batched check
-        for segment_id in segment_ids:
-            self.validate_segment_id(segment_id)
+        valid_count = Segment.objects.filter(
+            id__in=segment_ids, project_id=_environment(self.context).project_id
+        ).count()
+        if valid_count != len(segment_ids):
+            raise serializers.ValidationError(
+                "One or more segments not found in project"
+            )

Happy to open this as a follow-up if useful.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 39f7d2ea-874a-4876-9a95-d80d6825179b

📥 Commits

Reviewing files that changed from the base of the PR and between a7b0d21 and 8c25092.

📒 Files selected for processing (18)
  • api/api/urls/experiments.py
  • api/experimentation/services.py
  • api/features/feature_states/serializers.py
  • api/features/feature_states/types.py
  • api/features/feature_states/views.py
  • api/features/versioning/dataclasses.py
  • api/features/versioning/versioning_service.py
  • api/tests/integration/conftest.py
  • api/tests/integration/environments/identities/test_integration_identities.py
  • api/tests/integration/features/versioning/test_update_flag_endpoints.py
  • api/tests/integration/helpers.py
  • api/tests/unit/features/feature_states/test_serializers.py
  • api/tests/unit/features/feature_states/test_unit_feature_states_views.py
  • api/tests/unit/features/versioning/test_unit_versioning_versioning_service.py
  • docs/docs/deployment-self-hosting/observability/_events-catalogue.md
  • docs/docs/integrating-with-flagsmith/flagsmith-api-overview/admin-api/updating-flags.md
  • mcp/src/flagsmith_mcp/openapi.json
  • openapi.yaml

Comment thread api/features/feature_states/serializers.py
Comment thread api/features/versioning/versioning_service.py Outdated
Comment thread api/features/versioning/versioning_service.py Outdated
return attrs


class UpdateFlagOptionASerializer(BaseFeatureUpdateSerializer[FeatureState]):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Option A and Option B are becoming the new semantic for v1 and v2 ?

@emyller emyller Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To the public, they already are. The refactor here happens for:

  1. Eliminate ambiguity, i.e. flag-update v1/v2 over feature versioning v1/v2.
  2. Make it consistent with user docs.
  3. Improve clarity of naming of the backend components.
👻Only certain spirits are expected to have different names depending on the realm they're in, but these are just code.

@Zaimwa9 Zaimwa9 Jul 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah ok, I didn't have context over the public documentation. Makes sense, thanks for the clarification. I thought on the opposite this changes were a specific case of renaming versioning v1 versus v2.

Just a NIT then if I may:
It looks like this example of the doc is missing the second request in Option A
image

If my understanding is correct, the override should be updated in a separate request. If my understanding is wrong please ignore 👍

Feel free to resolve this thread

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks. Option A here is indeed one request, but the docs make it confusing. I've improved things with 42ea717 — also getting rid of the "highest=1" slop.

Please note these are unrelated to the scope of the PR, though I think the improvement warrants piggybacking.

@emyller
emyller marked this pull request as draft July 9, 2026 15:00
@github-actions github-actions Bot added docs Documentation updates feature New feature or request and removed feature New feature or request docs Documentation updates labels Jul 10, 2026
@emyller
emyller force-pushed the feat/multivariate-update-flag-endpoints branch from c4e9676 to 9d8dc5c Compare July 10, 2026 01:35
@github-actions github-actions Bot added docs Documentation updates feature New feature or request and removed feature New feature or request docs Documentation updates labels Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7ed23c6d-926a-4bdb-9501-ab0991c865b6

📥 Commits

Reviewing files that changed from the base of the PR and between 8c25092 and 9d8dc5c.

📒 Files selected for processing (18)
  • api/api/urls/experiments.py
  • api/experimentation/services.py
  • api/features/feature_states/serializers.py
  • api/features/feature_states/types.py
  • api/features/feature_states/views.py
  • api/features/versioning/dataclasses.py
  • api/features/versioning/versioning_service.py
  • api/tests/integration/conftest.py
  • api/tests/integration/environments/identities/test_integration_identities.py
  • api/tests/integration/features/versioning/test_update_flag_endpoints.py
  • api/tests/integration/helpers.py
  • api/tests/unit/features/feature_states/test_serializers.py
  • api/tests/unit/features/feature_states/test_unit_feature_states_views.py
  • api/tests/unit/features/versioning/test_unit_versioning_versioning_service.py
  • docs/docs/deployment-self-hosting/observability/_events-catalogue.md
  • docs/docs/integrating-with-flagsmith/flagsmith-api-overview/admin-api/updating-flags.md
  • mcp/src/flagsmith_mcp/openapi.json
  • openapi.yaml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
docs/docs/integrating-with-flagsmith/flagsmith-api-overview/admin-api/updating-flags.md (1)

235-238: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Clarify multivariate_options omission semantics.
State that omitting the field leaves existing options unchanged, while providing the array replaces the current set and deletes any omitted options.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f88b0d43-16e9-4175-9878-67217361caf2

📥 Commits

Reviewing files that changed from the base of the PR and between 9d8dc5c and 42ea717.

📒 Files selected for processing (7)
  • api/features/feature_states/serializers.py
  • api/features/feature_states/types.py
  • api/features/models.py
  • api/features/versioning/dataclasses.py
  • api/features/versioning/versioning_service.py
  • api/tests/integration/features/versioning/test_update_flag_endpoints.py
  • docs/docs/integrating-with-flagsmith/flagsmith-api-overview/admin-api/updating-flags.md

@khvn26 khvn26 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since the PR is still in draft, I've only reviewed the docs so far.

Set up multivariate flags and customise weights per segment.

- The `multivariate_options` list is absolute: omitting an option means deleting it.
- Segment overrides can only re-weight options configured in the environment.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this mean the user has to grab the IDs?

Why is re-weighting existing options important? I don't think we use the IDs for hashing anywhere?

@emyller emyller Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Does this mean the user has to grab the IDs?

Yes, unless we adopt variant keys (i.e. labels), which I attempted here but the diff increased quite a bit.

Why is re-weighting existing options important?

One major use case comes from experimentation. In fact, re-weighting segment MVs was the one thing already implemented before this PR (#7698).

I don't think we use the IDs for hashing anywhere?

Sorry, I don't understand this question in this context.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
api/experimentation/services.py (1)

596-631: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

_update_live_feature_state isn't self-contained atomic, unlike update_flag_option_a.

_update_live_feature_state saves enabled, then feature_state_value, then calls update_multivariate_values (which can raise ValidationError on over-allocation) as three separate, unwrapped writes. It's reached via _update_rollout_in_place's in-place-mutation branch, which deliberately bypasses update_flag_option_a (and therefore its transaction.atomic() wrap) to avoid re-randomising rollout variants.

Today this is safe only because both call sites either wrap the call in an outer transaction.atomic() (apply_experiment_rollout) or omit multivariate_values entirely (enable_experiment_rollout). This mirrors exactly the "direct caller not atomic" pattern already flagged and fixed for update_flag_option_a's predecessor — wrapping _update_live_feature_state itself would close the same class of risk defensively rather than relying on every future caller to remember to wrap it.

Proposed fix
+from django.db import transaction
+
 def _update_live_feature_state(
     feature_state: FeatureState, change_set: FlagChangeSetOptionA
 ) -> None:
-    if change_set.enabled is not None:
-        feature_state.enabled = change_set.enabled
-        feature_state.save()
-    if change_set.value is not None:
-        feature_state.feature_state_value.set_value(
-            change_set.value.value, change_set.value.type_
-        )
-        feature_state.feature_state_value.save()
-    update_multivariate_values(feature_state, change_set.multivariate_values)
+    with transaction.atomic():
+        if change_set.enabled is not None:
+            feature_state.enabled = change_set.enabled
+            feature_state.save()
+        if change_set.value is not None:
+            feature_state.feature_state_value.set_value(
+                change_set.value.value, change_set.value.type_
+            )
+            feature_state.feature_state_value.save()
+        update_multivariate_values(feature_state, change_set.multivariate_values)
api/tests/unit/features/versioning/test_unit_versioning_versioning_service.py (1)

857-857: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test name doesn't reflect the Option A rename.

The body now exercises update_flag_option_a/FlagChangeSetOptionA, but the test retains its pre-rename name (test_update_flag__segment_override_with_mv__sets_mv_values) instead of the _option_a suffix used consistently by its Option B siblings (e.g. test_update_flag_option_b__...). Minor inconsistency with the PR's stated Option A/B taxonomy standardisation.

✏️ Suggested rename
-def test_update_flag__segment_override_with_mv__sets_mv_values(
+def test_update_flag_option_a__segment_override_with_mv__sets_mv_values(

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8b5b21a3-3cb1-4c22-a5b3-72b81368b095

📥 Commits

Reviewing files that changed from the base of the PR and between 656a5ca and 7141260.

📒 Files selected for processing (21)
  • api/api/urls/experiments.py
  • api/experimentation/serializers.py
  • api/experimentation/services.py
  • api/features/feature_states/serializers.py
  • api/features/feature_states/types.py
  • api/features/feature_states/views.py
  • api/features/models.py
  • api/features/versioning/dataclasses.py
  • api/features/versioning/versioning_service.py
  • api/tests/integration/conftest.py
  • api/tests/integration/environments/identities/test_integration_identities.py
  • api/tests/integration/features/versioning/test_update_flag_endpoints.py
  • api/tests/integration/helpers.py
  • api/tests/unit/experimentation/test_services.py
  • api/tests/unit/features/feature_states/test_serializers.py
  • api/tests/unit/features/feature_states/test_unit_feature_states_views.py
  • api/tests/unit/features/versioning/test_unit_versioning_versioning_service.py
  • docs/docs/deployment-self-hosting/observability/_events-catalogue.md
  • docs/docs/integrating-with-flagsmith/flagsmith-api-overview/admin-api/updating-flags.md
  • mcp/src/flagsmith_mcp/openapi.json
  • openapi.yaml

@khvn26 khvn26 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we need to align on our approach to internal versioning APIs before we can proceed further.

def update_flag_v1(request: Request, environment_key: str) -> Response:
def update_flag_option_a(request: Request, environment_key: str) -> Response:
environment = Environment.objects.get(api_key=environment_key)
_check_workflow_not_enabled(environment)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Note: I wonder if, for the future, we could think of creating a change request here, and returning its id (perhaps under 202 Accepted).

Comment on lines +600 to +607
if change_set.enabled is not None:
feature_state.enabled = change_set.enabled
feature_state.save()
if change_set.value is not None:
feature_state.feature_state_value.set_value(
change_set.value.value, change_set.value.type_
)
feature_state.feature_state_value.save()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we verify that resulting audit logs make sense?

Comment on lines +465 to +472
if environment_default.enabled is not None:
env_default_state.enabled = environment_default.enabled
env_default_state.save()

if environment_default.value is not None:
_update_feature_state_value(
env_default_state.feature_state_value, environment_default.value
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same question as here.

enabled: bool | None,
value: FeatureValue | None,
get_environment_default: typing.Callable[[], FeatureState],
) -> tuple[bool, FeatureStateValue | None]:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would using the Django model as a DTO work here? Just want to steer away from anonymous tuples.

Suggested change
) -> tuple[bool, FeatureStateValue | None]:
) -> FeatureState | None:

Comment on lines +182 to +188
enabled, inherited_value = _resolve_new_override_inheritance(
change_set.enabled,
change_set.value,
lambda: new_version.feature_states.get(
feature_segment__isnull=True, identity_id=None
),
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looking at this, I feel like we deserve a first-class versioning API among the lines of

def add_to_version(efv: EnvironmentFeatureVersion, feature_state: FeatureState) -> None: ...

...where FeatureState is a memory-only ORM object, or an equivalent DTO.

This API could optionally accept segment and identity arguments.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you feel it would take too much additional time, feel free to not do it, as long as the other comment is addressed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This entire module is a headache to read through in its current state. I'm blocking this PR until we sort where we're heading with this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As discussed offline, we should toss out option A in favour of option B.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Issue related to the REST API feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants