Skip to content

Commit

Permalink
fix: POST /identities incorrectly applies segment overrides when us…
Browse files Browse the repository at this point in the history
…ing null or empty identifier, only in Core API (#5018)
  • Loading branch information
khvn26 authored Jan 20, 2025
1 parent b056831 commit 3557e14
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 1 deletion.
6 changes: 5 additions & 1 deletion api/environments/identities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ def get_all_feature_states(

# define sub queries
belongs_to_environment_query = Q(environment=self.environment)
overridden_for_identity_query = Q(identity=self)
if self.id:
overridden_for_identity_query = Q(identity=self)
else:
# skip identity overrides for transient identities
overridden_for_identity_query = Q()
overridden_for_segment_query = Q(
feature_segment__segment__in=segments,
feature_segment__environment=self.environment,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,3 +468,115 @@ def test_get_feature_states_for_identity__transient_trait__existing_identity__re
"transient": True,
},
]


def test_get_feature_states_for_identity__transient_identifier__empty_segment__return_expected(
admin_client: APIClient,
sdk_client: APIClient,
default_feature_value: str,
identity_identifier: str,
feature: int,
environment: int,
identity: int,
project: id,
) -> None:
# Given
# a %0 segment that matches no identity
response = admin_client.post(
reverse("api-v1:projects:project-segments-list", args=[project]),
data=json.dumps(
{
"name": "empty-segment",
"project": project,
"rules": [
{
"type": "ALL",
"rules": [
{
"type": "ANY",
"rules": [],
"conditions": [
{
"operator": "PERCENTAGE_SPLIT",
"value": 0,
}
],
}
],
"conditions": [],
}
],
}
),
content_type="application/json",
)
segment_id = response.json()["id"]

# and a segment override for the %0 segment
response = admin_client.post(
reverse("api-v1:features:feature-segment-list"),
data=json.dumps(
{
"feature": feature,
"segment": segment_id,
"environment": environment,
}
),
content_type="application/json",
)
feature_segment_id = response.json()["id"]

admin_client.post(
reverse("api-v1:features:featurestates-list"),
data=json.dumps(
{
"enabled": True,
"feature_state_value": {
"type": "unicode",
"string_value": "segment override",
},
"feature": feature,
"environment": environment,
"feature_segment": feature_segment_id,
}
),
content_type="application/json",
)

url = reverse("api-v1:sdk-identities")

# When
# flags are requested for a transient identifier
response = sdk_client.post(
url,
data=json.dumps(
{
"identifier": "",
"traits": [
{
"trait_key": "transient",
"trait_value": "trait value",
},
],
}
),
content_type="application/json",
)

# Then
assert response.status_code == status.HTTP_200_OK
response_json = response.json()

assert (
flag_data := next(
(
flag
for flag in response_json["flags"]
if flag["feature"]["id"] == feature
),
None,
)
)
# flag is not being overridden by the segment
assert flag_data["enabled"] is False
assert flag_data["feature_state_value"] == default_feature_value

0 comments on commit 3557e14

Please sign in to comment.