Skip to content

Commit 173e666

Browse files
fix: reuse DynamoEnvironmentWrapper in DynamoIdentityWrapper (#6813)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 74c391b commit 173e666

5 files changed

Lines changed: 74 additions & 26 deletions

File tree

api/environments/dynamodb/migrator.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ def migrate(self): # type: ignore[no-untyped-def]
6060
api_keys = EnvironmentAPIKey.objects.filter(environment__project_id=project_id)
6161
api_key_wrapper.write_api_keys(api_keys)
6262

63-
identity_wrapper = DynamoIdentityWrapper()
63+
identity_wrapper = DynamoIdentityWrapper(
64+
environment_wrapper=environment_wrapper
65+
)
6466
identities = (
6567
Identity.objects.filter(environment__project__id=project_id)
6668
.select_related("environment")

api/environments/dynamodb/wrappers/identity_wrapper.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@
3737

3838

3939
class DynamoIdentityWrapper(BaseDynamoWrapper):
40+
def __init__(
41+
self,
42+
environment_wrapper: DynamoEnvironmentWrapper | None = None,
43+
) -> None:
44+
super().__init__()
45+
self._environment_wrapper = environment_wrapper or DynamoEnvironmentWrapper()
46+
4047
def get_table_name(self) -> str | None: # type: ignore[override]
4148
return settings.IDENTITIES_TABLE_NAME_DYNAMO
4249

@@ -188,9 +195,8 @@ def get_segment_ids(
188195
identity = identity_model or IdentityModel.model_validate(
189196
self.get_item_from_uuid(identity_pk)
190197
)
191-
environment_wrapper = DynamoEnvironmentWrapper()
192198
environment = EnvironmentModel.model_validate(
193-
environment_wrapper.get_item(identity.environment_api_key)
199+
self._environment_wrapper.get_item(identity.environment_api_key)
194200
)
195201
context = map_environment_identity_to_context(
196202
environment=environment,

api/environments/tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def delete_environment_from_dynamo(api_key: str, environment_id: str): # type:
5050
environment_wrapper.delete_environment(api_key)
5151

5252
# Delete identities
53-
identity_wrapper = DynamoIdentityWrapper()
53+
identity_wrapper = DynamoIdentityWrapper(environment_wrapper=environment_wrapper)
5454
identity_wrapper.delete_all_identities(api_key)
5555

5656
# Delete environment_v2 documents

api/tests/unit/environments/dynamodb/test_unit_migrator.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ def test_migrate_calls_internal_methods_with_correct_arguments( # type: ignore[
4040
identity_migrator.migrate() # type: ignore[no-untyped-call]
4141

4242
# Then
43-
mocked_identity_wrapper.assert_called_with()
43+
mocked_identity_wrapper.assert_called_with(
44+
environment_wrapper=mocked_environment_wrapper.return_value
45+
)
4446

4547
args, kwargs = mocked_identity_wrapper.return_value.write_identities.call_args
4648
assert kwargs == {}

api/tests/unit/environments/dynamodb/wrappers/test_unit_dynamodb_identity_wrapper.py

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -312,18 +312,19 @@ def test_get_segment_ids_returns_correct_segment_ids( # type: ignore[no-untyped
312312
Segment.objects.create(name="Non matching segment", project=project)
313313

314314
identity_document = map_identity_to_identity_document(identity)
315-
dynamo_identity_wrapper = DynamoIdentityWrapper()
316-
mocked_get_item_from_uuid = mocker.patch.object(
317-
dynamo_identity_wrapper, "get_item_from_uuid", return_value=identity_document
318-
)
319315
identity_uuid = identity_document["identity_uuid"]
320-
321316
environment_document = map_environment_to_environment_document(environment)
317+
322318
mocked_environment_wrapper = mocker.patch(
323319
"environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
324320
)
325321
mocked_environment_wrapper.return_value.get_item.return_value = environment_document
326322

323+
dynamo_identity_wrapper = DynamoIdentityWrapper()
324+
mocked_get_item_from_uuid = mocker.patch.object(
325+
dynamo_identity_wrapper, "get_item_from_uuid", return_value=identity_document
326+
)
327+
327328
# When
328329
segment_ids = dynamo_identity_wrapper.get_segment_ids(identity_uuid) # type: ignore[arg-type]
329330

@@ -386,18 +387,19 @@ def test_get_segment_ids_with_segment_feature_overrides(
386387
)
387388

388389
identity_document = map_identity_to_identity_document(identity)
389-
dynamo_identity_wrapper = DynamoIdentityWrapper()
390-
mocker.patch.object(
391-
dynamo_identity_wrapper, "get_item_from_uuid", return_value=identity_document
392-
)
393390
identity_uuid = identity_document["identity_uuid"]
394-
395391
environment_document = map_environment_to_environment_document(environment)
392+
396393
mocked_environment_wrapper = mocker.patch(
397394
"environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
398395
)
399396
mocked_environment_wrapper.return_value.get_item.return_value = environment_document
400397

398+
dynamo_identity_wrapper = DynamoIdentityWrapper()
399+
mocker.patch.object(
400+
dynamo_identity_wrapper, "get_item_from_uuid", return_value=identity_document
401+
)
402+
401403
# When
402404
segment_ids = dynamo_identity_wrapper.get_segment_ids(identity_uuid) # type: ignore[arg-type]
403405

@@ -427,18 +429,19 @@ def test_get_segment_ids_returns_segment_using_in_operator_for_integer_traits(
427429
)
428430

429431
identity_document = map_identity_to_identity_document(identity)
430-
dynamo_identity_wrapper = DynamoIdentityWrapper()
431-
mocker.patch.object(
432-
dynamo_identity_wrapper, "get_item_from_uuid", return_value=identity_document
433-
)
434432
identity_uuid = identity_document["identity_uuid"]
435-
436433
environment_document = map_environment_to_environment_document(environment)
434+
437435
mocked_environment_wrapper = mocker.patch(
438436
"environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
439437
)
440438
mocked_environment_wrapper.return_value.get_item.return_value = environment_document
441439

440+
dynamo_identity_wrapper = DynamoIdentityWrapper()
441+
mocker.patch.object(
442+
dynamo_identity_wrapper, "get_item_from_uuid", return_value=identity_document
443+
)
444+
442445
# When
443446
segment_ids = dynamo_identity_wrapper.get_segment_ids(identity_uuid) # type: ignore[arg-type]
444447

@@ -490,20 +493,20 @@ def test_get_segment_ids_throws_value_error_if_arguments_not_valid(): # type: i
490493

491494
def test_get_segment_ids_with_identity_model(identity, environment, mocker): # type: ignore[no-untyped-def]
492495
# Given
493-
dynamo_identity_wrapper = DynamoIdentityWrapper()
494496
identity_document = map_identity_to_identity_document(identity)
495497
identity_model = IdentityModel.parse_obj(identity_document)
496-
497-
mocker.patch.object(
498-
dynamo_identity_wrapper, "get_item_from_uuid", return_value=identity_document
499-
)
500-
501498
environment_document = map_environment_to_environment_document(environment)
499+
502500
mocked_environment_wrapper = mocker.patch(
503501
"environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
504502
)
505503
mocked_environment_wrapper.return_value.get_item.return_value = environment_document
506504

505+
dynamo_identity_wrapper = DynamoIdentityWrapper()
506+
mocker.patch.object(
507+
dynamo_identity_wrapper, "get_item_from_uuid", return_value=identity_document
508+
)
509+
507510
# When
508511
segment_ids = dynamo_identity_wrapper.get_segment_ids(identity_model=identity_model)
509512

@@ -640,6 +643,41 @@ def test_identity_wrapper__iter_all_items_paginated__capacity_budget_set__raises
640643
)
641644

642645

646+
def test_get_segment_ids__called_multiple_times__reuses_environment_wrapper(
647+
project: "Project",
648+
environment: "Environment",
649+
identity: "Identity",
650+
mocker: "MockerFixture",
651+
) -> None:
652+
# Given
653+
identity_document = map_identity_to_identity_document(identity)
654+
environment_document = map_environment_to_environment_document(environment)
655+
656+
mocked_environment_wrapper_class = mocker.patch(
657+
"environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
658+
)
659+
mocked_environment_wrapper_class.return_value.get_item.return_value = (
660+
environment_document
661+
)
662+
663+
dynamo_identity_wrapper = DynamoIdentityWrapper()
664+
mocker.patch.object(
665+
dynamo_identity_wrapper,
666+
"get_item_from_uuid",
667+
return_value=identity_document,
668+
)
669+
identity_uuid = str(identity_document["identity_uuid"])
670+
671+
# When
672+
dynamo_identity_wrapper.get_segment_ids(identity_uuid)
673+
dynamo_identity_wrapper.get_segment_ids(identity_uuid)
674+
dynamo_identity_wrapper.get_segment_ids(identity_uuid)
675+
676+
# Then - DynamoEnvironmentWrapper should be instantiated once
677+
# (during DynamoIdentityWrapper.__init__), not once per get_segment_ids call.
678+
assert mocked_environment_wrapper_class.call_count == 1
679+
680+
643681
def test_delete_all_identities__deletes_all_identities_documents_from_dynamodb(
644682
flagsmith_identities_table: Table,
645683
dynamodb_identity_wrapper: DynamoIdentityWrapper,

0 commit comments

Comments
 (0)