Skip to content

Commit

Permalink
Preserve null values for save with fields passed
Browse files Browse the repository at this point in the history
  • Loading branch information
ruscoder committed Aug 27, 2024
1 parent 0a9ca27 commit a3be32e
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 78 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.0.7

* Preserve null values for resource in save() with fields passed

## 2.0.6

* Fix type inference for client.resource
Expand Down
2 changes: 1 addition & 1 deletion fhirpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .lib import AsyncFHIRClient, SyncFHIRClient

__title__ = "fhir-py"
__version__ = "2.0.6"
__version__ = "2.0.7"
__author__ = "beda.software"
__license__ = "None"
__copyright__ = "Copyright 2024 beda.software"
Expand Down
10 changes: 3 additions & 7 deletions fhirpy/base/lib_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ async def save(
# _as_dict is a private api used internally
_as_dict: bool = False,
) -> Union[TResource, Any]:
data = serialize(resource)
data = serialize(resource, drop_dict_null_values=fields is None)
if fields:
if not resource.id:
raise TypeError("Resource `id` is required for update operation")
Expand Down Expand Up @@ -287,13 +287,9 @@ async def update(self) -> TResource: # type: ignore

async def patch(self, **kwargs) -> TResource:
if not self.id:
raise TypeError("Resource `id` is required for delete operation")
raise TypeError("Resource `id` is required for patch operation")
super(BaseResource, self).update(**kwargs)
response_data = await self.__client__.patch(self.reference, **kwargs)

resource_type = self.resource_type
super(BaseResource, self).clear()
super(BaseResource, self).update(**self.__client__.resource(resource_type, **response_data))
await self.save(fields=list(kwargs.keys()))

return cast(TResource, self)

Expand Down
10 changes: 3 additions & 7 deletions fhirpy/base/lib_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def save(
# _as_dict is a private api used internally
_as_dict: bool = False,
) -> Union[TResource, Any]:
data = serialize(resource)
data = serialize(resource, drop_dict_null_values=fields is None)
if fields:
if not resource.id:
raise TypeError("Resource `id` is required for update operation")
Expand Down Expand Up @@ -281,13 +281,9 @@ def update(self) -> TResource: # type: ignore

def patch(self, **kwargs) -> TResource:
if not self.id:
raise TypeError("Resource `id` is required for delete operation")
raise TypeError("Resource `id` is required for patch operation")
super(BaseResource, self).update(**kwargs)
response_data = self.__client__.patch(self.reference, **kwargs)

resource_type = self.resource_type
super(BaseResource, self).clear()
super(BaseResource, self).update(**self.__client__.resource(resource_type, **response_data))
self.save(fields=list(kwargs.keys()))

return cast(TResource, self)

Expand Down
73 changes: 41 additions & 32 deletions tests/test_lib_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,20 +135,26 @@ async def test_client_save_existing(self):

@pytest.mark.asyncio()
async def test_client_save_partial_update(self):
patient = await self.create_patient_model()
patient = await self.create_patient_model(
managingOrganization=Reference(reference="urn:organization")
)

patient.identifier = [
*patient.identifier,
Identifier(system="url", value="value"),
]
patient.name[0].text = "New patient"
patient.managingOrganization = None

updated_patient = await self.client.save(patient, fields=["identifier"])
updated_patient = await self.client.save(
patient, fields=["identifier", "managingOrganization"]
)

assert isinstance(updated_patient, Patient)
assert updated_patient.id == patient.id
assert len(updated_patient.identifier) == 2 # noqa: PLR2004
assert updated_patient.name[0].text == "My patient"
assert updated_patient.managingOrganization is None

@pytest.mark.asyncio()
async def test_client_save_partial_update_fails_without_id(self):
Expand Down Expand Up @@ -408,7 +414,7 @@ async def test_conditional_operations__fail_on_multiple_matches(self):
)

@pytest.mark.asyncio()
async def test_update_with_params__no_match(self):
async def test_conditional_update__no_match(self):
patient = await self.create_resource("Patient", id="patient", active=True)

patient_to_update = self.client.resource(
Expand All @@ -427,7 +433,7 @@ async def test_update_with_params__no_match(self):
assert created is True

@pytest.mark.asyncio()
async def test_update_with_params__one_match(self):
async def test_conditional_update__one_match(self):
patient = await self.create_resource("Patient", id="patient", active=True)

patient_to_update = self.client.resource(
Expand All @@ -450,7 +456,7 @@ async def test_update_with_params__one_match(self):
assert patient.get("active") is None

@pytest.mark.asyncio()
async def test_patch_with_params__no_match(self):
async def test_conditional_patch__no_match(self):
patient_to_patch = self.client.resource(
"Patient",
identifier=[{"system": "http://example.com/env", "value": "other"}, self.identifier[0]],
Expand All @@ -462,7 +468,7 @@ async def test_patch_with_params__no_match(self):
)

@pytest.mark.asyncio()
async def test_patch_with_params__one_match(self):
async def test_conditional_patch__one_match(self):
patient = await self.create_resource(
"Patient",
id="patient",
Expand Down Expand Up @@ -494,7 +500,7 @@ async def test_patch_with_params__one_match(self):
assert patient.get("managingOrganization") is None

@pytest.mark.asyncio()
async def test_patch_with_params__one_match_deprecated(self):
async def test_conditional_patch__one_match_deprecated(self):
patient = await self.create_resource("Patient", id="patient", active=True)

patient_to_patch = self.client.resource(
Expand Down Expand Up @@ -569,7 +575,7 @@ async def test_delete_without_id_failed(self):
await patient.delete()

@pytest.mark.asyncio()
async def test_delete_with_params__no_match(self):
async def test_conditional_delete__no_match(self):
await self.create_resource("Patient", id="patient")

_, status_code = await self.client.resources("Patient").search(identifier="other").delete()
Expand All @@ -578,7 +584,7 @@ async def test_delete_with_params__no_match(self):
assert status_code == 204 # noqa: PLR2004

@pytest.mark.asyncio()
async def test_delete_with_params__one_match(self):
async def test_conditional_delete__one_match(self):
patient = self.client.resource(
"Patient",
id="patient",
Expand All @@ -595,7 +601,7 @@ async def test_delete_with_params__one_match(self):
assert status_code == 200 # noqa: PLR2004

@pytest.mark.asyncio()
async def test_delete_with_params__multiple_matches(self):
async def test_conditional_delete__multiple_matches(self):
await self.create_resource("Patient", id="patient-1")
await self.create_resource("Patient", id="patient-2")

Expand Down Expand Up @@ -859,18 +865,42 @@ async def test_save_fields(self):
active=False,
birthDate="1998-01-01",
name=[{"text": "Abc"}],
managingOrganization={"reference": "urn:organization"},
)
patient["gender"] = "male"
patient["birthDate"] = "1998-02-02"
patient["active"] = True
patient["name"] = [{"text": "Bcd"}]
await patient.save(fields=["gender", "birthDate"])
patient["managingOrganization"] = None
await patient.save(fields=["gender", "birthDate", "managingOrganization"])

patient_refreshed = await patient.to_reference().to_resource()
assert patient_refreshed["gender"] == patient["gender"]
assert patient_refreshed["birthDate"] == patient["birthDate"]
assert patient_refreshed["active"] is False
assert patient_refreshed["name"] == [{"text": "Abc"}]
assert patient_refreshed.get("managingOrganization") is None

@pytest.mark.asyncio()
async def test_update_patch_without_id(self):
patient = self.client.resource(
"Patient", identifier=self.identifier, name=[{"text": "J London"}]
)
new_name = [
{
"text": "Jack London",
"family": "London",
"given": ["Jack"],
}
]
with pytest.raises(TypeError):
await patient.update()
with pytest.raises(TypeError):
await patient.patch(active=True, name=new_name)
patient["name"] = new_name
with pytest.raises(TypeError):
await patient.save(fields=["name"])
await patient.save()

@pytest.mark.asyncio()
async def test_update(self):
Expand Down Expand Up @@ -918,27 +948,6 @@ async def test_patch(self):
assert patient_instance_1_refreshed["name"] == new_name
assert patient_instance_1_refreshed.get("managingOrganization") is None

@pytest.mark.asyncio()
async def test_update_without_id(self):
patient = self.client.resource(
"Patient", identifier=self.identifier, name=[{"text": "J London"}]
)
new_name = [
{
"text": "Jack London",
"family": "London",
"given": ["Jack"],
}
]
with pytest.raises(TypeError):
await patient.update()
with pytest.raises(TypeError):
await patient.patch(active=True, name=new_name)
patient["name"] = new_name
with pytest.raises(TypeError):
await patient.save(fields=["name"])
await patient.save()

@pytest.mark.asyncio()
async def test_refresh(self):
patient_id = "refresh-patient-id"
Expand Down
Loading

0 comments on commit a3be32e

Please sign in to comment.