Skip to content

Commit d02dc4b

Browse files
author
Sergio García Prado
authored
Merge pull request #454 from minos-framework/issue-453-add-snapshot-find-one
#453 - Add `find_one` method to `SnapshotRepository`
2 parents 97ca4f7 + f7ae22e commit d02dc4b

File tree

5 files changed

+122
-2
lines changed

5 files changed

+122
-2
lines changed

packages/core/minos-microservice-aggregate/minos/aggregate/entities/repositories.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ def get_all(
9797
# noinspection PyTypeChecker
9898
return self._snapshot_repository.get_all(type_, ordering, limit, **kwargs)
9999

100+
async def find_one(self, type_: type[T], condition: _Condition, **kwargs) -> T:
101+
"""Find a ``Entity`` instance based on a ``Condition``.
102+
103+
:param type_: The type of the entity to be looked for.
104+
:param condition: The condition that must be satisfied by the ``Entity`` instances.
105+
:param kwargs: Additional named arguments.
106+
:return: An asynchronous iterator that containing the ``Entity`` instances.
107+
"""
108+
return await self._snapshot_repository.find_one(type_, condition, **kwargs)
109+
100110
def find(
101111
self,
102112
type_: type[T],
@@ -107,15 +117,14 @@ def find(
107117
) -> AsyncIterator[T]:
108118
"""Find a collection of instances based on a given ``Condition``.
109119
110-
:param type_: The of the entity to be looked for.
120+
:param type_: The type of the entity to be looked for.
111121
:param condition: The ``Condition`` that must be satisfied by all the instances.
112122
:param ordering: Optional argument to return the instance with specific ordering strategy. The default behaviour
113123
is to retrieve them without any order pattern.
114124
:param limit: Optional argument to return only a subset of instances. The default behaviour is to return all the
115125
instances that meet the given condition.
116126
:return: An asynchronous iterator of ``Entity`` instances.
117127
"""
118-
# noinspection PyTypeChecker
119128
return self._snapshot_repository.find(type_, condition, ordering, limit, **kwargs)
120129

121130
async def create(self, type_or_instance: Union[T, type[T]], *args, **kwargs) -> tuple[T, Delta]:

packages/core/minos-microservice-aggregate/minos/aggregate/snapshots/repositories/abc.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,28 @@ def get_all(
126126
**kwargs,
127127
)
128128

129+
async def find_one(
130+
self,
131+
name: Union[str, type[Entity]],
132+
condition: _Condition,
133+
transaction: Optional[TransactionEntry] = None,
134+
**kwargs,
135+
) -> Entity:
136+
"""Find a ``Entity`` instance based on a ``Condition``.
137+
138+
:param name: Class name of the ``Entity``.
139+
:param condition: The condition that must be satisfied by the ``Entity`` instances.
140+
:param transaction: The transaction within the operation is performed. If not any value is provided, then the
141+
transaction is extracted from the context var. If not any transaction is being scoped then the query is
142+
performed to the global snapshot.
143+
:param kwargs: Additional named arguments.
144+
:return: An asynchronous iterator that containing the ``Entity`` instances.
145+
"""
146+
try:
147+
return await self.find(name, condition=condition, limit=1, transaction=transaction, **kwargs).__anext__()
148+
except StopAsyncIteration:
149+
raise NotFoundException(f"There are not any instance matching the given condition: {condition}")
150+
129151
async def find(
130152
self,
131153
name: Union[str, type[Entity], ModelType],

packages/core/minos-microservice-aggregate/minos/aggregate/testing/snapshots/repositories/testcases.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,39 @@ async def test_find_empty(self):
532532
expected = set()
533533
self.assertEqual(expected, observed)
534534

535+
async def test_find_one(self):
536+
await self.populate_and_synchronize()
537+
condition = Condition.IN("uuid", {self.uuid_2})
538+
539+
observed = await self.snapshot_repository.find_one(self.Car, condition)
540+
541+
expected = self.Car(
542+
3,
543+
"blue",
544+
uuid=self.uuid_2,
545+
version=2,
546+
created_at=observed.created_at,
547+
updated_at=observed.updated_at,
548+
)
549+
self.assertEqual(expected, observed)
550+
551+
async def test_find_one_raises(self):
552+
await self.populate_and_synchronize()
553+
condition = Condition.FALSE
554+
555+
with self.assertRaises(NotFoundException):
556+
await self.snapshot_repository.find_one(self.Car, condition)
557+
558+
async def test_get_all(self):
559+
await self.populate_and_synchronize()
560+
iterable = self.snapshot_repository.find(self.Car, Condition.TRUE, ordering=Ordering.ASC("updated_at"))
561+
expected = [v async for v in iterable]
562+
563+
iterable = self.snapshot_repository.get_all(self.Car, ordering=Ordering.ASC("updated_at"))
564+
observed = [v async for v in iterable]
565+
566+
self.assertEqual(expected, observed)
567+
535568
async def test_get(self):
536569
await self.populate_and_synchronize()
537570
observed = await self.snapshot_repository.get(self.Car, self.uuid_2)

packages/core/minos-microservice-aggregate/tests/test_aggregate/test_entities/test_repositories/test_base.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,23 @@ async def test_find(self):
110110

111111
self.assertEqual(originals, recovered)
112112

113+
async def test_find_one(self):
114+
originals = list(
115+
map(
116+
itemgetter(0),
117+
await gather(
118+
self.repository.create(Car, doors=3, color="blue"),
119+
self.repository.create(Car, doors=3, color="red"),
120+
self.repository.create(Car, doors=5, color="blue"),
121+
),
122+
)
123+
)
124+
condition = Condition.IN("uuid", {originals[0].uuid})
125+
observed = await self.repository.find_one(Car, condition)
126+
expected = originals[0]
127+
128+
self.assertEqual(expected, observed)
129+
113130
async def test_get(self):
114131
original, _ = await self.repository.create(Car, doors=3, color="blue")
115132
recovered = await self.repository.get(Car, original.uuid)

packages/core/minos-microservice-aggregate/tests/test_aggregate/test_snapshots/test_repositories/test_database.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,45 @@ async def test_find_empty(self):
414414
):
415415
await super().test_find_empty()
416416

417+
async def test_find_one(self):
418+
entities = [
419+
SnapshotRepositoryTestCase.Car(3, "blue", uuid=self.uuid_2, version=2),
420+
]
421+
with patch.object(DatabaseClient, "fetch_one", return_value=(9999,)):
422+
with patch.object(
423+
DatabaseClient,
424+
"fetch_all",
425+
return_value=FakeAsyncIterator(
426+
[tuple(SnapshotEntry.from_entity(entity).as_raw().values()) for entity in entities]
427+
),
428+
):
429+
await super().test_find_one()
430+
431+
async def test_find_one_raises(self):
432+
with patch.object(DatabaseClient, "fetch_one", return_value=(9999,)):
433+
with patch.object(
434+
DatabaseClient,
435+
"fetch_all",
436+
return_value=FakeAsyncIterator([]),
437+
):
438+
await super().test_find_one_raises()
439+
440+
async def test_get_all(self):
441+
entities = [
442+
SnapshotRepositoryTestCase.Car(3, "blue", uuid=self.uuid_1, version=1),
443+
SnapshotRepositoryTestCase.Car(3, "blue", uuid=self.uuid_2, version=2),
444+
SnapshotRepositoryTestCase.Car(3, "blue", uuid=self.uuid_3, version=1),
445+
]
446+
with patch.object(DatabaseClient, "fetch_one", return_value=(9999,)):
447+
with patch.object(
448+
DatabaseClient,
449+
"fetch_all",
450+
side_effect=lambda: FakeAsyncIterator(
451+
[tuple(SnapshotEntry.from_entity(entity).as_raw().values()) for entity in entities]
452+
),
453+
):
454+
await super().test_get_all()
455+
417456
async def test_get(self):
418457
entities = [
419458
SnapshotRepositoryTestCase.Car(3, "blue", uuid=self.uuid_2, version=2),

0 commit comments

Comments
 (0)