diff --git a/api/edge_api/identities/serializers.py b/api/edge_api/identities/serializers.py index fa1bc4897d26..72dd73d58262 100644 --- a/api/edge_api/identities/serializers.py +++ b/api/edge_api/identities/serializers.py @@ -285,11 +285,14 @@ class GetEdgeIdentityOverridesQuerySerializer(serializers.Serializer): class EdgeIdentitySearchField(serializers.CharField): def to_internal_value(self, data: str) -> EdgeIdentitySearchData: kwargs = {} - search_term = data.lower() + search_term = data if search_term.startswith(DASHBOARD_ALIAS_SEARCH_PREFIX): kwargs["search_attribute"] = DASHBOARD_ALIAS_ATTRIBUTE - search_term = search_term.lstrip(DASHBOARD_ALIAS_SEARCH_PREFIX) + # dashboard aliases are always stored in lower case + search_term = search_term.removeprefix( + DASHBOARD_ALIAS_SEARCH_PREFIX + ).lower() else: kwargs["search_attribute"] = IDENTIFIER_ATTRIBUTE diff --git a/api/tests/integration/conftest.py b/api/tests/integration/conftest.py index b5d79c20155c..7afc966ccdb8 100644 --- a/api/tests/integration/conftest.py +++ b/api/tests/integration/conftest.py @@ -375,6 +375,9 @@ def identity_document( mv_feature_name, mv_feature, ): + _identifier = "User1-Test" # use a mixture of cases and symbols to make sure we're testing all cases + _dashboard_alias = "dashboard-alias" + _environment_feature_state_1_document = { "featurestate_uuid": "ad71c644-71df-4e83-9cb5-cd2cd0160200", "multivariate_feature_state_values": [], @@ -431,15 +434,15 @@ def identity_document( "feature_segment": None, } return { - "composite_key": f"{environment_api_key}_user_1_test", - "dashboard_alias": "dashboard-alias", + "composite_key": f"{environment_api_key}_{_identifier}", + "dashboard_alias": _dashboard_alias, "identity_traits": identity_traits, "identity_features": [ _environment_feature_state_1_document, _environment_feature_state_2_document, _mv_feature_state_document, ], - "identifier": "user_1_test", + "identifier": _identifier, "created_date": "2021-09-21T10:12:42.230257+00:00", "environment_api_key": environment_api_key, "identity_uuid": "59efa2a7-6a45-46d6-b953-a7073a90eacf", diff --git a/api/tests/integration/edge_api/identities/test_edge_identity_viewset.py b/api/tests/integration/edge_api/identities/test_edge_identity_viewset.py index 963516be0f2f..b479c86ef9ce 100644 --- a/api/tests/integration/edge_api/identities/test_edge_identity_viewset.py +++ b/api/tests/integration/edge_api/identities/test_edge_identity_viewset.py @@ -9,12 +9,7 @@ from rest_framework.exceptions import NotFound from rest_framework.test import APIClient -from edge_api.identities.search import ( - DASHBOARD_ALIAS_SEARCH_PREFIX, - IDENTIFIER_ATTRIBUTE, - EdgeIdentitySearchData, - EdgeIdentitySearchType, -) +from edge_api.identities.search import DASHBOARD_ALIAS_SEARCH_PREFIX from environments.dynamodb.wrappers.environment_wrapper import ( DynamoEnvironmentV2Wrapper, ) @@ -259,54 +254,43 @@ def test_get_identities_list( def test_search_identities_without_exact_match( - admin_client, - dynamo_enabled_environment, - environment_api_key, - identity_document, - edge_identity_dynamo_wrapper_mock, + admin_client: APIClient, + dynamo_enabled_environment: Environment, + environment_api_key: str, + identity_document: dict[str, Any], + flagsmith_identities_table: Table, ): # Given identifier = identity_document["identifier"] + flagsmith_identities_table.put_item(Item=identity_document) + base_url = reverse( "api-v1:environments:environment-edge-identities-list", args=[environment_api_key], ) - url = "%s?q=%s" % (base_url, identifier) - edge_identity_dynamo_wrapper_mock.search_items.return_value = { - "Items": [identity_document], - "Count": 1, - } + url = "%s?q=%s" % (base_url, identifier[:6]) # When response = admin_client.get(url) + # Then assert response.status_code == status.HTTP_200_OK - assert response.json()["results"][0]["identifier"] == identifier assert len(response.json()["results"]) == 1 - - edge_identity_dynamo_wrapper_mock.search_items.assert_called_with( - environment_api_key=environment_api_key, - search_data=EdgeIdentitySearchData( - search_term=identifier, - search_type=EdgeIdentitySearchType.BEGINS_WITH, - search_attribute=IDENTIFIER_ATTRIBUTE, - ), - limit=100, - start_key=None, - ) + assert response.json()["results"][0]["identifier"] == identifier def test_search_for_identities_with_exact_match( - admin_client, - dynamo_enabled_environment, - environment_api_key, - identity_document, - edge_identity_dynamo_wrapper_mock, + admin_client: APIClient, + dynamo_enabled_environment: Environment, + environment_api_key: str, + identity_document: dict[str, Any], + flagsmith_identities_table: Table, ): # Given identifier = identity_document["identifier"] + flagsmith_identities_table.put_item(Item=identity_document) base_url = reverse( "api-v1:environments:environment-edge-identities-list", @@ -316,31 +300,56 @@ def test_search_for_identities_with_exact_match( base_url, urllib.parse.urlencode({"q": f'"{identifier}"'}), ) - edge_identity_dynamo_wrapper_mock.search_items.return_value = { - "Items": [identity_document], - "Count": 1, - } # When response = admin_client.get(url) + # Then assert response.status_code == status.HTTP_200_OK - assert response.json()["results"][0]["identifier"] == identifier assert len(response.json()["results"]) == 1 + assert response.json()["results"][0]["identifier"] == identifier + + +def test_search_for_identities_by_dashboard_alias_prefix( + admin_client: APIClient, + dynamo_enabled_environment: Environment, + environment_api_key: str, + identity_document: dict[str, Any], + flagsmith_identities_table: Table, +) -> None: + # Given + identifier = identity_document["identifier"] + + # This test verifies a previous bug which meant that if any of the + # leading characters to the search string were contained in the + # `string `"dashboard_alias:"` then they would also be removed + # and the search would fail. + identity_document["dashboard_alias"] = "hans.gruber@example.com" + search_string = "hans" - edge_identity_dynamo_wrapper_mock.search_items.assert_called_with( - environment_api_key=environment_api_key, - search_data=EdgeIdentitySearchData( - search_term=identifier, - search_type=EdgeIdentitySearchType.EQUAL, - search_attribute=IDENTIFIER_ATTRIBUTE, + flagsmith_identities_table.put_item(Item=identity_document) + + base_url = reverse( + "api-v1:environments:environment-edge-identities-list", + args=[environment_api_key], + ) + url = "%s?%s" % ( + base_url, + urllib.parse.urlencode( + {"q": f"{DASHBOARD_ALIAS_SEARCH_PREFIX}{search_string}"} ), - limit=100, - start_key=None, ) + # When + response = admin_client.get(url) -def test_search_for_identities_by_dashboard_alias( + # Then + assert response.status_code == status.HTTP_200_OK + assert len(response.json()["results"]) == 1 + assert response.json()["results"][0]["identifier"] == identifier + + +def test_search_for_identities_by_dashboard_alias_exact( admin_client: APIClient, dynamo_enabled_environment: Environment, environment_api_key: str, @@ -369,8 +378,8 @@ def test_search_for_identities_by_dashboard_alias( # Then assert response.status_code == status.HTTP_200_OK - assert response.json()["results"][0]["identifier"] == identifier assert len(response.json()["results"]) == 1 + assert response.json()["results"][0]["identifier"] == identifier def test_search_for_identities_by_dashboard_alias_casts_search_to_lower( @@ -402,8 +411,8 @@ def test_search_for_identities_by_dashboard_alias_casts_search_to_lower( # Then assert response.status_code == status.HTTP_200_OK - assert response.json()["results"][0]["identifier"] == identifier assert len(response.json()["results"]) == 1 + assert response.json()["results"][0]["identifier"] == identifier def test_update_edge_identity(