Skip to content

Commit

Permalink
Add .execute for search set #74
Browse files Browse the repository at this point in the history
  • Loading branch information
ruscoder committed Aug 5, 2024
1 parent 2f20657 commit 63be0db
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* It's a private API, there's a small chance that it should not affect the code
* Deprecate conditional patch with resource argument, use kwargs instead
* Fix pickling error for BaseResource instances #77
* Add .execute for search set #74

## 1.4.2
* Conditional delete @pavlushkin
Expand Down
46 changes: 33 additions & 13 deletions fhirpy/base/lib_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ async def to_resource(self) -> TResource:
return cast(TResource, self)

async def is_valid(self, raise_exception=False) -> bool:
data = await self.__client__._do_request(
"post", f"{self.resource_type}/$validate", data=self.serialize()
data = await self.__client__.execute(
f"{self.resource_type}/$validate", method="post", data=self.serialize()
)
if any(issue["severity"] in ["fatal", "error"] for issue in data["issue"]):
if raise_exception:
Expand All @@ -288,9 +288,9 @@ async def execute(
data: Union[dict, None] = None,
params: Union[dict, None] = None,
) -> Any:
return await self.__client__._do_request(
method,
return await self.__client__.execute(
f"{self._get_path()}/{operation}",
method,
data=data,
params=params,
)
Expand All @@ -308,16 +308,22 @@ async def to_resource(self) -> TResource:
"""
if not self.is_local:
raise ResourceNotFound("Can not resolve not local resource")
resource_data = await self.__client__._do_request("get", f"{self.resource_type}/{self.id}")
resource_data = await self.__client__.execute(
f"{self.resource_type}/{self.id}", method="get"
)
return self._dict_to_resource(resource_data)

async def execute(self, operation, method="post", **kwargs):
async def execute(
self,
operation,
method="post",
data: Union[dict, None] = None,
params: Union[dict, None] = None,
):
if not self.is_local:
raise ResourceNotFound("Can not execute on not local resource")
return await self.__client__._do_request(
method,
f"{self.resource_type}/{self.id}/{operation}",
**kwargs,
return await self.__client__.execute(
f"{self.resource_type}/{self.id}/{operation}", method=method, data=data, params=params
)

async def patch(self, **kwargs) -> TResource:
Expand All @@ -331,6 +337,20 @@ async def delete(self):
class AsyncSearchSet(
Generic[TAsyncClient, TResource], AbstractSearchSet[TAsyncClient, TResource], ABC
):
async def execute(
self,
operation: str,
method: str = "post",
data: Union[dict, None] = None,
params: Union[dict, None] = None,
) -> Any:
return await self.client.execute(
f"{self.resource_type}/{operation}",
method=method,
data=data,
params=params,
)

async def fetch(self) -> list[TResource]:
bundle_data = await self.client._fetch_resource(self.resource_type, self.params)

Expand Down Expand Up @@ -383,7 +403,7 @@ async def first(self) -> Union[TResource, None]:
async def get_or_create(self, resource: TResource) -> tuple[TResource, bool]:
assert resource.resourceType == self.resource_type
response_data, status_code = await self.client._do_request(
"POST",
"post",
self.resource_type,
serialize(resource),
self.params,
Expand All @@ -396,7 +416,7 @@ async def update(self, resource: TResource) -> tuple[TResource, bool]:
# accordingly to the https://build.fhir.org/http.html#cond-update
assert resource.resourceType == self.resource_type
response_data, status_code = await self.client._do_request(
"PUT",
"put",
self.resource_type,
serialize(resource),
self.params,
Expand All @@ -420,7 +440,7 @@ async def patch(self, _resource: Any = None, **kwargs) -> TResource:

async def delete(self) -> Any:
return await self.client._do_request(
"DELETE", self.resource_type, params=self.params, returning_status=True
"delete", self.resource_type, params=self.params, returning_status=True
)

async def __aiter__(self) -> AsyncGenerator[TResource, None]:
Expand Down
50 changes: 37 additions & 13 deletions fhirpy/base/lib_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@ def to_resource(self) -> TResource:
return cast(TResource, self)

def is_valid(self, raise_exception=False) -> bool:
data = self.__client__._do_request(
"post", f"{self.resource_type}/$validate", data=self.serialize()
data = self.__client__.execute(
f"{self.resource_type}/$validate", method="post", data=self.serialize()
)
if any(issue["severity"] in ["fatal", "error"] for issue in data["issue"]):
if raise_exception:
Expand All @@ -282,9 +282,9 @@ def execute(
data: Union[dict, None] = None,
params: Union[dict, None] = None,
) -> Any:
return self.__client__._do_request(
method,
return self.__client__.execute(
f"{self._get_path()}/{operation}",
method=method,
data=data,
params=params,
)
Expand All @@ -302,16 +302,26 @@ def to_resource(self) -> TResource:
"""
if not self.is_local:
raise ResourceNotFound("Can not resolve not local resource")
resource_data = self.__client__._do_request("get", f"{self.resource_type}/{self.id}")
resource_data = self.__client__.execute(
f"{self.resource_type}/{self.id}",
method="get",
)
return self._dict_to_resource(resource_data)

def execute(self, operation, method="post", **kwargs):
def execute(
self,
operation,
method="post",
data: Union[dict, None] = None,
params: Union[dict, None] = None,
):
if not self.is_local:
raise ResourceNotFound("Can not execute on not local resource")
return self.__client__._do_request(
method,
return self.__client__.execute(
f"{self.resource_type}/{self.id}/{operation}",
**kwargs,
method=method,
data=data,
params=params,
)

def patch(self, **kwargs) -> TResource:
Expand All @@ -325,6 +335,20 @@ def delete(self):
class SyncSearchSet(
Generic[TSyncClient, TResource], AbstractSearchSet[TSyncClient, TResource], ABC
):
def execute(
self,
operation: str,
method: str = "post",
data: Union[dict, None] = None,
params: Union[dict, None] = None,
) -> Any:
return self.client.execute(
f"{self.resource_type}/{operation}",
method=method,
data=data,
params=params,
)

def fetch(self) -> list[TResource]:
bundle_data = self.client._fetch_resource(self.resource_type, self.params)

Expand Down Expand Up @@ -377,7 +401,7 @@ def first(self) -> Union[TResource, None]:
def get_or_create(self, resource: TResource) -> tuple[TResource, int]:
assert resource.resourceType == self.resource_type
response_data, status_code = self.client._do_request(
"POST",
"post",
self.resource_type,
serialize(resource),
self.params,
Expand All @@ -390,7 +414,7 @@ def update(self, resource: TResource) -> tuple[TResource, int]:
# accordingly to the https://build.fhir.org/http.html#cond-update
assert resource.resourceType == self.resource_type
response_data, status_code = self.client._do_request(
"PUT",
"put",
self.resource_type,
serialize(resource),
self.params,
Expand All @@ -409,12 +433,12 @@ def patch(self, _resource: Any = None, **kwargs) -> TResource:
)

data = serialize(_resource if _resource is not None else kwargs)
response_data = self.client._do_request("PATCH", self.resource_type, data, self.params)
response_data = self.client._do_request("patch", self.resource_type, data, self.params)
return self._dict_to_resource(response_data)

def delete(self) -> Any:
return self.client._do_request(
"DELETE", self.resource_type, params=self.params, returning_status=True
"delete", self.resource_type, params=self.params, returning_status=True
)

def __iter__(self) -> Generator[TResource, None, None]:
Expand Down
8 changes: 7 additions & 1 deletion fhirpy/base/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,13 @@ def to_resource(self):
pass

@abstractmethod
def execute(self, operation, method=None, **kwargs):
def execute(
self,
operation,
method=None,
data: Union[dict, None] = None,
params: Union[dict, None] = None,
):
pass

@abstractmethod
Expand Down
10 changes: 10 additions & 0 deletions fhirpy/base/searchset.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ def _dict_to_resource(self, data) -> TResource:
return self.custom_resource_class(**data)
return self.client.resource(data["resourceType"], **data)

@abstractmethod
def execute(
self,
path: str,
method: str = "post",
data: Union[dict, None] = None,
params: Union[dict, None] = None,
):
pass

@abstractmethod
def fetch(self):
pass
Expand Down
29 changes: 29 additions & 0 deletions tests/test_lib_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,35 @@ async def test_client_execute_lastn(self):
assert response["total"] == 1
assert response["entry"][0]["resource"]["id"] == observation["id"]

@pytest.mark.asyncio()
async def test_searchset_execute_lastn(self):
patient = await self.create_resource("Patient", name=[{"text": "John First"}])
observation = await self.create_resource(
"Observation",
status="registered",
subject=patient,
category=[
{
"coding": [
{
"code": "vital-signs",
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"display": "Vital Signs",
}
]
}
],
code={"coding": [{"code": "10000-8", "system": "http://loinc.org"}]},
)
response = await self.client.resources("Observation").execute(
"$lastn",
method="get",
params={"patient": f"Patient/{patient.id}", "category": "vital-signs"},
)
assert response["resourceType"] == "Bundle"
assert response["total"] == 1
assert response["entry"][0]["resource"]["id"] == observation["id"]

@pytest.mark.asyncio()
async def test_resource_execute_lastn(self):
patient = await self.create_resource("Patient", name=[{"text": "John First"}])
Expand Down
28 changes: 28 additions & 0 deletions tests/test_lib_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,34 @@ def test_client_execute_lastn(self):
assert response["total"] == 1
assert response["entry"][0]["resource"]["id"] == observation["id"]

def test_searchset_execute_lastn(self):
patient = self.create_resource("Patient", name=[{"text": "John First"}])
observation = self.create_resource(
"Observation",
status="registered",
subject=patient,
category=[
{
"coding": [
{
"code": "vital-signs",
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"display": "Vital Signs",
}
]
}
],
code={"coding": [{"code": "10000-8", "system": "http://loinc.org"}]},
)
response = self.client.resources("Observation").execute(
"$lastn",
method="get",
params={"patient": f"Patient/{patient.id}", "category": "vital-signs"},
)
assert response["resourceType"] == "Bundle"
assert response["total"] == 1
assert response["entry"][0]["resource"]["id"] == observation["id"]

def test_resource_execute_lastn(self):
patient = self.create_resource("Patient", name=[{"text": "John First"}])
observation = self.create_resource(
Expand Down

0 comments on commit 63be0db

Please sign in to comment.