Skip to content

Commit 0f801c6

Browse files
Merge pull request #22 from minvws/feat/query-providers
feat: query healthcare provider on specific params
2 parents 44d0f98 + d90280e commit 0f801c6

7 files changed

Lines changed: 220 additions & 33 deletions

File tree

app/db/repository/healthcare_provider.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import Sequence
22
from uuid import UUID
33

44
from sqlalchemy import and_, select, update
@@ -20,11 +20,6 @@ def add_one(self, data: HealthcareProviderEntity) -> HealthcareProviderEntity:
2020
self.db_session.rollback()
2121
raise e
2222

23-
def get_all(self) -> List[HealthcareProviderEntity]:
24-
stmt = select(HealthcareProviderEntity).where(HealthcareProviderEntity.deleted_at.is_(None))
25-
results = self.db_session.session.execute(stmt).scalars().all()
26-
return list(results)
27-
2823
def get_one(self, id: UUID) -> HealthcareProviderEntity | None:
2924
stmt = select(HealthcareProviderEntity).where(
3025
and_(
@@ -35,6 +30,31 @@ def get_one(self, id: UUID) -> HealthcareProviderEntity | None:
3530
results = self.db_session.session.execute(stmt).scalar()
3631
return results
3732

33+
def get_many(
34+
self,
35+
oin: str | None = None,
36+
source_id: str | None = None,
37+
ura_number: str | None = None,
38+
) -> Sequence[HealthcareProviderEntity]:
39+
conditions = []
40+
41+
if oin:
42+
conditions.append((HealthcareProviderEntity.oin == oin))
43+
44+
if source_id:
45+
conditions.append((HealthcareProviderEntity.source_id == source_id))
46+
47+
if ura_number is not None:
48+
conditions.append((HealthcareProviderEntity.ura_number == ura_number))
49+
50+
stmt = (
51+
select(HealthcareProviderEntity)
52+
.where(and_(*conditions))
53+
.where(HealthcareProviderEntity.deleted_at.is_(None))
54+
)
55+
results = self.db_session.session.execute(stmt).scalars().all()
56+
return results
57+
3858
def update(self, id: UUID, **kwargs: object) -> HealthcareProviderEntity | None:
3959
try:
4060
target = {k: kwargs[k] for k in HealthcareProviderEntity.__table__.columns.keys() if k in kwargs}

app/models/params.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import Any
2+
3+
from pydantic import BaseModel, field_validator
4+
5+
from app.models.ura_number import UraNumber
6+
7+
8+
class HealthcareProvidersQueryParams(BaseModel):
9+
oin: str | None = None
10+
source_id: str | None = None
11+
ura_number: str | None = None
12+
13+
@field_validator("ura_number")
14+
@classmethod
15+
def validate_ura_number(cls, value: Any) -> str:
16+
try:
17+
result = UraNumber(value)
18+
except ValueError as e:
19+
raise ValueError(f"Invalid UraNumber: {e}")
20+
21+
return result.value

app/routers/healthcare_provider.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import logging
2-
from typing import Annotated, Any
2+
from typing import Annotated, Any, List
33
from uuid import UUID
44

5-
from fastapi import APIRouter, Body, Depends, HTTPException, Response
5+
from fastapi import APIRouter, Body, Depends, HTTPException, Query, Response
66

77
from app.container import get_healthcare_provider_service
88
from app.models.healthcare_provider import (
99
HealthcareProvider,
1010
HealthcareProviderCreate,
1111
HealthcareProviderUpdate,
1212
)
13+
from app.models.params import HealthcareProvidersQueryParams
1314
from app.services.healthcare_provider import HeatlhcareProviderService
1415

1516
logger = logging.getLogger(__name__)
@@ -25,6 +26,15 @@ def add_one_provider(
2526
return new_provider
2627

2728

29+
@router.get("", response_model=List[HealthcareProvider], response_model_exclude_none=True)
30+
def get(
31+
params: Annotated[HealthcareProvidersQueryParams, Query()],
32+
service: Annotated[HeatlhcareProviderService, Depends(get_healthcare_provider_service)],
33+
) -> Any:
34+
# TODO: update this is functional_management
35+
return service.get(**params.model_dump())
36+
37+
2838
@router.get("/{id}", response_model=HealthcareProvider, response_model_exclude_none=True)
2939
def get_by_id(
3040
id: UUID,
@@ -37,14 +47,6 @@ def get_by_id(
3747
return results
3848

3949

40-
@router.get("", response_model=list[HealthcareProvider], response_model_exclude_none=True)
41-
def get_all(
42-
service: Annotated[HeatlhcareProviderService, Depends(get_healthcare_provider_service)],
43-
) -> Any:
44-
results = service.get_all()
45-
return results
46-
47-
4850
@router.delete("/{id}")
4951
def delete_by_id(
5052
id: UUID,

app/services/healthcare_provider.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime
2+
from typing import List
23
from uuid import UUID
34

45
from app.db.db import Database
@@ -17,12 +18,16 @@ def get_one(self, id: UUID) -> HealthcareProviderEntity | None:
1718

1819
return provider
1920

20-
def get_all(self) -> list[HealthcareProviderEntity]:
21+
def get(
22+
self,
23+
oin: str | None = None,
24+
source_id: str | None = None,
25+
ura_number: str | None = None,
26+
) -> List[HealthcareProviderEntity]:
2127
with self.db.get_db_session() as session:
2228
repository = session.get_repository(HealthcareProvidersRepository)
23-
providers = repository.get_all()
24-
25-
return providers
29+
healthcare_provider = repository.get_many(oin, source_id, ura_number)
30+
return list(healthcare_provider)
2631

2732
def create_one(
2833
self,

tests/db/test_healthcare_provider.py

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from app.db.models.healthcare_provider import HealthcareProviderEntity
44
from app.db.repository.healthcare_provider import HealthcareProvidersRepository
5+
from app.models.ura_number import UraNumber
56

67

78
def test_add_one_success(
@@ -79,23 +80,90 @@ def test_update_not_found(
7980
assert result is None
8081

8182

82-
def test_get_all(
83+
def test_get_many_should_return_all_records(
8384
healthcare_provider_repository: HealthcareProvidersRepository,
8485
healthcare_provider_entity: HealthcareProviderEntity,
86+
ura_number: UraNumber,
8587
) -> None:
88+
entity_2 = HealthcareProviderEntity(
89+
ura_number=ura_number.value,
90+
source_id="some_other_source_id",
91+
is_source=True,
92+
is_viewer=True,
93+
oin="some_oin",
94+
common_name="some_common_name",
95+
status="active",
96+
)
97+
entity_3 = HealthcareProviderEntity(
98+
ura_number=UraNumber("00000125").value,
99+
source_id="antoher-source-id",
100+
is_source=True,
101+
is_viewer=False,
102+
oin="antoher-oin",
103+
common_name="fake-name",
104+
status="active",
105+
)
86106
with healthcare_provider_repository.db_session:
87107
healthcare_provider_repository.add_one(healthcare_provider_entity)
108+
healthcare_provider_repository.add_one(entity_2)
109+
healthcare_provider_repository.add_one(entity_3)
88110

89-
result = healthcare_provider_repository.get_all()
111+
results = healthcare_provider_repository.get_many()
90112

91-
assert len(result) == 1
92-
assert result[0].id == healthcare_provider_entity.id
113+
assert len(results) == 3
93114

94115

95-
def test_get_all_returns_empty_list_when_no_data(
116+
def test_get_many_should_return_exact_as_per_query(
96117
healthcare_provider_repository: HealthcareProvidersRepository,
118+
healthcare_provider_entity: HealthcareProviderEntity,
119+
ura_number: UraNumber,
97120
) -> None:
121+
entity_2 = HealthcareProviderEntity(
122+
ura_number=ura_number.value,
123+
source_id="some_other_source_id",
124+
is_source=True,
125+
is_viewer=True,
126+
oin="some_oin",
127+
common_name="some_common_name",
128+
status="active",
129+
)
130+
entity_3 = HealthcareProviderEntity(
131+
ura_number=UraNumber("00000125").value,
132+
source_id="antoher-source-id",
133+
is_source=True,
134+
is_viewer=False,
135+
oin="antoher-oin",
136+
common_name="fake-name",
137+
status="active",
138+
)
98139
with healthcare_provider_repository.db_session:
99-
result = healthcare_provider_repository.get_all()
140+
healthcare_provider_repository.add_one(healthcare_provider_entity)
141+
healthcare_provider_repository.add_one(entity_2)
142+
healthcare_provider_repository.add_one(entity_3)
143+
144+
results = healthcare_provider_repository.get_many(ura_number=ura_number.value, oin="some_oin")
145+
146+
assert len(results) == 2
147+
148+
results = healthcare_provider_repository.get_many(
149+
ura_number=ura_number.value,
150+
oin=healthcare_provider_entity.oin,
151+
source_id=healthcare_provider_entity.source_id,
152+
)
153+
assert len(results) == 1
154+
155+
156+
def test_many_should_return_empty_list_when_no_match_is_found(
157+
healthcare_provider_repository: HealthcareProvidersRepository,
158+
healthcare_provider_entity: HealthcareProviderEntity,
159+
) -> None:
160+
with healthcare_provider_repository.db_session:
161+
healthcare_provider_repository.add_one(healthcare_provider_entity)
162+
163+
results = healthcare_provider_repository.get_many(
164+
ura_number="00000157",
165+
source_id="some-unknown-source",
166+
oin="000012345678910",
167+
)
100168

101-
assert result == []
169+
assert len(results) == 0

tests/models/test_params.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import pytest
2+
3+
from app.models.params import HealthcareProvidersQueryParams
4+
from app.models.ura_number import UraNumber
5+
6+
7+
def test_serialize_should_succeed(ura_number: UraNumber) -> None:
8+
expected = {
9+
"oin": "000000912345678",
10+
"source_id": "some-id",
11+
"ura_number": ura_number.value,
12+
}
13+
data = HealthcareProvidersQueryParams(oin="000000912345678", source_id="some-id", ura_number=ura_number.value)
14+
15+
actual = data.model_dump()
16+
17+
assert expected == actual
18+
19+
20+
def test_deserialize_should_succeed(ura_number: UraNumber) -> None:
21+
expected = HealthcareProvidersQueryParams(oin="000000912345678", source_id="some-id", ura_number=ura_number.value)
22+
data = {
23+
"oin": "000000912345678",
24+
"source_id": "some-id",
25+
"ura_number": ura_number.value,
26+
}
27+
28+
actual = HealthcareProvidersQueryParams.model_validate(data)
29+
30+
assert expected == actual
31+
32+
33+
def test_deserialize_should_raise_exception_with_invalid_ura() -> None:
34+
with pytest.raises(ValueError) as exc:
35+
HealthcareProvidersQueryParams(oin="0000123456789", source_id="some-id", ura_number="INVALID-URA")
36+
37+
assert "Invalid UraNumber" in str(exc.value)

tests/services/test_healthcare_provider_service.py

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def test_update_one_should_return_none_when_nothing_to_update(
147147
assert expected is None
148148

149149

150-
def test_get_all_should_succeed(
150+
def test_get_should_succeed_and_return_all_records(
151151
healthcare_provider_service: HeatlhcareProviderService,
152152
healthcare_provider_entity: HealthcareProviderEntity,
153153
) -> None:
@@ -170,20 +170,20 @@ def test_get_all_should_succeed(
170170
status=healthcare_provider_entity.status,
171171
)
172172

173-
expected = healthcare_provider_service.get_all()
173+
expected = healthcare_provider_service.get()
174174
assert len(expected) == 2
175175
assert expected[0].id == data.id
176176
assert expected[1].id == data2.id
177177

178178

179-
def test_get_all_should_return_empty_list_when_no_data(
179+
def test_get_should_return_empty_list_when_no_data(
180180
healthcare_provider_service: HeatlhcareProviderService,
181181
) -> None:
182-
expected = healthcare_provider_service.get_all()
182+
expected = healthcare_provider_service.get()
183183
assert expected == []
184184

185185

186-
def test_get_all_should_return_only_non_deleted_data(
186+
def test_get_should_return_only_non_deleted_data(
187187
healthcare_provider_service: HeatlhcareProviderService,
188188
healthcare_provider_entity: HealthcareProviderEntity,
189189
) -> None:
@@ -198,5 +198,39 @@ def test_get_all_should_return_only_non_deleted_data(
198198
)
199199
healthcare_provider_service.delete_one(data.id)
200200

201-
expected = healthcare_provider_service.get_all()
201+
expected = healthcare_provider_service.get()
202202
assert expected == []
203+
204+
205+
def test_get_should_return_exact_as_per_args(
206+
healthcare_provider_service: HeatlhcareProviderService,
207+
healthcare_provider_entity: HealthcareProviderEntity,
208+
) -> None:
209+
data = healthcare_provider_service.create_one(
210+
ura_number=healthcare_provider_entity.ura_number,
211+
source_id=healthcare_provider_entity.source_id,
212+
is_source=healthcare_provider_entity.is_source,
213+
is_viewer=healthcare_provider_entity.is_viewer,
214+
oin=healthcare_provider_entity.oin,
215+
common_name=healthcare_provider_entity.common_name,
216+
status=healthcare_provider_entity.status,
217+
)
218+
healthcare_provider_service.create_one(
219+
ura_number="00000456",
220+
source_id="some-other-source-id",
221+
is_source=True,
222+
is_viewer=True,
223+
oin="some-other-oin",
224+
common_name="some-other-common-name",
225+
status="active",
226+
)
227+
228+
results = healthcare_provider_service.get(oin=data.oin, source_id=data.source_id)
229+
230+
assert len(results) == 1
231+
assert results[0].id == data.id
232+
for key in data.__table__.columns.keys():
233+
expected_attr = getattr(results[0], key)
234+
actual_attr = getattr(data, key)
235+
236+
assert expected_attr == actual_attr

0 commit comments

Comments
 (0)