helix.fhir.client.sdk 4.2.3__py3-none-any.whl → 4.2.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. helix_fhir_client_sdk/fhir_auth_mixin.py +17 -10
  2. helix_fhir_client_sdk/fhir_client.py +152 -79
  3. helix_fhir_client_sdk/fhir_delete_mixin.py +62 -48
  4. helix_fhir_client_sdk/fhir_merge_mixin.py +188 -166
  5. helix_fhir_client_sdk/fhir_merge_resources_mixin.py +200 -15
  6. helix_fhir_client_sdk/fhir_patch_mixin.py +97 -84
  7. helix_fhir_client_sdk/fhir_update_mixin.py +71 -57
  8. helix_fhir_client_sdk/graph/simulated_graph_processor_mixin.py +0 -24
  9. helix_fhir_client_sdk/open_telemetry/__init__.py +0 -0
  10. helix_fhir_client_sdk/open_telemetry/attribute_names.py +7 -0
  11. helix_fhir_client_sdk/open_telemetry/span_names.py +12 -0
  12. helix_fhir_client_sdk/queue/request_queue_mixin.py +17 -12
  13. helix_fhir_client_sdk/responses/fhir_client_protocol.py +10 -6
  14. helix_fhir_client_sdk/responses/fhir_get_response.py +1 -1
  15. helix_fhir_client_sdk/responses/fhir_response_processor.py +73 -54
  16. helix_fhir_client_sdk/responses/get/fhir_get_bundle_response.py +1 -1
  17. helix_fhir_client_sdk/responses/get/fhir_get_error_response.py +0 -1
  18. helix_fhir_client_sdk/responses/get/fhir_get_list_by_resource_type_response.py +1 -1
  19. helix_fhir_client_sdk/responses/get/fhir_get_list_response.py +1 -1
  20. helix_fhir_client_sdk/responses/get/fhir_get_response_factory.py +0 -1
  21. helix_fhir_client_sdk/responses/get/fhir_get_single_response.py +1 -1
  22. helix_fhir_client_sdk/responses/merge/fhir_merge_resource_response_entry.py +30 -0
  23. helix_fhir_client_sdk/utilities/cache/request_cache.py +32 -43
  24. helix_fhir_client_sdk/utilities/retryable_aiohttp_client.py +185 -154
  25. helix_fhir_client_sdk/utilities/retryable_aiohttp_response.py +2 -1
  26. helix_fhir_client_sdk/validators/async_fhir_validator.py +3 -0
  27. helix_fhir_client_sdk-4.2.18.dist-info/METADATA +200 -0
  28. {helix_fhir_client_sdk-4.2.3.dist-info → helix_fhir_client_sdk-4.2.18.dist-info}/RECORD +35 -28
  29. tests/async/test_benchmark_compress.py +448 -0
  30. tests/async/test_benchmark_merge.py +506 -0
  31. tests/async/test_retryable_client_session_management.py +159 -0
  32. tests/test_fhir_client_clone.py +155 -0
  33. helix_fhir_client_sdk-4.2.3.dist-info/METADATA +0 -115
  34. {helix_fhir_client_sdk-4.2.3.dist-info → helix_fhir_client_sdk-4.2.18.dist-info}/WHEEL +0 -0
  35. {helix_fhir_client_sdk-4.2.3.dist-info → helix_fhir_client_sdk-4.2.18.dist-info}/licenses/LICENSE +0 -0
  36. {helix_fhir_client_sdk-4.2.3.dist-info → helix_fhir_client_sdk-4.2.18.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,155 @@
1
+ """
2
+ Tests for FhirClient.clone() method to ensure all properties are properly copied.
3
+ """
4
+
5
+ from datetime import UTC, datetime, timedelta
6
+
7
+ from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode import (
8
+ CompressedDictStorageMode,
9
+ )
10
+
11
+ from helix_fhir_client_sdk.fhir_client import FhirClient
12
+
13
+
14
+ def test_clone_preserves_compress_setting() -> None:
15
+ """Test that clone() preserves the _compress setting"""
16
+ # Create a client with compression disabled
17
+ fhir_client = FhirClient().url("http://example.com").compress(False)
18
+ assert fhir_client._compress is False
19
+
20
+ # Clone and verify compression is still disabled
21
+ cloned_client = fhir_client.clone()
22
+ assert cloned_client._compress is False
23
+
24
+ # Test with compression enabled
25
+ fhir_client2 = FhirClient().url("http://example.com").compress(True)
26
+ assert fhir_client2._compress is True
27
+
28
+ cloned_client2 = fhir_client2.clone()
29
+ assert cloned_client2._compress is True
30
+
31
+
32
+ def test_clone_preserves_storage_mode() -> None:
33
+ """Test that clone() preserves the _storage_mode setting"""
34
+ # Create a client with msgpack storage mode
35
+ storage_mode = CompressedDictStorageMode(storage_type="msgpack")
36
+ fhir_client = FhirClient().url("http://example.com").set_storage_mode(storage_mode)
37
+ assert fhir_client._storage_mode.storage_type == "msgpack"
38
+
39
+ # Clone and verify storage mode is preserved
40
+ cloned_client = fhir_client.clone()
41
+ assert cloned_client._storage_mode.storage_type == "msgpack"
42
+
43
+
44
+ def test_clone_preserves_additional_settings() -> None:
45
+ """Test that clone() preserves other compression-related settings"""
46
+ fhir_client = (
47
+ FhirClient()
48
+ .url("http://example.com")
49
+ .compress(False)
50
+ .send_data_as_chunked(True)
51
+ .use_post_for_search(True)
52
+ .maximum_time_to_retry_on_429(120)
53
+ .retry_count(5)
54
+ .throw_exception_on_error(False)
55
+ .set_log_all_response_urls(True)
56
+ .set_create_operation_outcome_for_error(True)
57
+ )
58
+
59
+ cloned_client = fhir_client.clone()
60
+
61
+ assert cloned_client._compress is False
62
+ assert cloned_client._send_data_as_chunked is True
63
+ assert cloned_client._use_post_for_search is True
64
+ assert cloned_client._maximum_time_to_retry_on_429 == 120
65
+ assert cloned_client._retry_count == 5
66
+ assert cloned_client._throw_exception_on_error is False
67
+ assert cloned_client._log_all_response_urls is True
68
+ assert cloned_client._create_operation_outcome_for_error is True
69
+
70
+
71
+ def test_default_compression_is_enabled() -> None:
72
+ """Test that compression is enabled by default"""
73
+ fhir_client = FhirClient()
74
+ assert fhir_client._compress is True
75
+
76
+
77
+ def test_default_storage_mode_is_raw() -> None:
78
+ """Test that the default storage mode is 'raw'"""
79
+ fhir_client = FhirClient()
80
+ assert fhir_client._storage_mode.storage_type == "raw"
81
+
82
+
83
+ def test_clone_preserves_access_token() -> None:
84
+ """Test that clone() preserves access token and expiry date"""
85
+ expiry_date = datetime.now(UTC) + timedelta(hours=1)
86
+ fhir_client = (
87
+ FhirClient()
88
+ .url("http://example.com")
89
+ .set_access_token("test-access-token-12345")
90
+ .set_access_token_expiry_date(expiry_date)
91
+ )
92
+
93
+ assert fhir_client._access_token == "test-access-token-12345"
94
+ assert fhir_client._access_token_expiry_date == expiry_date
95
+
96
+ # Clone and verify access token is preserved
97
+ cloned_client = fhir_client.clone()
98
+
99
+ assert cloned_client._access_token == "test-access-token-12345"
100
+ assert cloned_client._access_token_expiry_date == expiry_date
101
+
102
+
103
+ def test_clone_preserves_max_concurrent_requests() -> None:
104
+ """Test that clone() preserves max_concurrent_requests setting"""
105
+ fhir_client = FhirClient().url("http://example.com").set_max_concurrent_requests(10)
106
+
107
+ assert fhir_client._max_concurrent_requests == 10
108
+
109
+ # Clone and verify max_concurrent_requests is preserved
110
+ cloned_client = fhir_client.clone()
111
+
112
+ assert cloned_client._max_concurrent_requests == 10
113
+
114
+
115
+ def test_use_http_session() -> None:
116
+ """Test that use_http_session sets the callable correctly"""
117
+ from aiohttp import ClientSession
118
+
119
+ def custom_session_factory() -> ClientSession:
120
+ return ClientSession()
121
+
122
+ fhir_client = FhirClient().url("http://example.com")
123
+
124
+ # Initially None
125
+ assert fhir_client._fn_create_http_session is None
126
+
127
+ # Set the callable
128
+ result = fhir_client.use_http_session(custom_session_factory)
129
+
130
+ # Returns self for chaining
131
+ assert result is fhir_client
132
+
133
+ # Callable is stored
134
+ assert fhir_client._fn_create_http_session is custom_session_factory
135
+
136
+ # Can set to None
137
+ fhir_client.use_http_session(None)
138
+ assert fhir_client._fn_create_http_session is None
139
+
140
+
141
+ def test_clone_preserves_fn_create_http_session() -> None:
142
+ """Test that clone() preserves the _fn_create_http_session callable"""
143
+ from aiohttp import ClientSession
144
+
145
+ def custom_session_factory() -> ClientSession:
146
+ return ClientSession()
147
+
148
+ fhir_client = FhirClient().url("http://example.com").use_http_session(custom_session_factory)
149
+
150
+ assert fhir_client._fn_create_http_session is custom_session_factory
151
+
152
+ # Clone and verify fn_create_http_session is preserved
153
+ cloned_client = fhir_client.clone()
154
+
155
+ assert cloned_client._fn_create_http_session is custom_session_factory
@@ -1,115 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: helix.fhir.client.sdk
3
- Version: 4.2.3
4
- Summary: helix.fhir.client.sdk
5
- Home-page: https://github.com/icanbwell/helix.fhir.client.sdk
6
- Author: Imran Qureshi
7
- Author-email: imran@icanbwell.com
8
- Classifier: Development Status :: 4 - Beta
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: Apache Software License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.10
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
- Requires-Dist: furl
16
- Requires-Dist: requests
17
- Requires-Dist: urllib3
18
- Requires-Dist: chardet
19
- Requires-Dist: aiohttp
20
- Requires-Dist: async-timeout>=4.0.3
21
- Requires-Dist: python-dateutil
22
- Requires-Dist: compressedfhir>=1.0.3
23
- Dynamic: author
24
- Dynamic: author-email
25
- Dynamic: classifier
26
- Dynamic: description
27
- Dynamic: description-content-type
28
- Dynamic: home-page
29
- Dynamic: license-file
30
- Dynamic: requires-dist
31
- Dynamic: requires-python
32
- Dynamic: summary
33
-
34
- # helix.fhir.client.sdk
35
-
36
- <p align="left">
37
- <a href="https://github.com/icanbwell/helix.fhir.client.sdk/actions">
38
- <img src="https://github.com/icanbwell/helix.fhir.client.sdk/workflows/Build%20and%20Test/badge.svg"
39
- alt="Continuous Integration">
40
- </a>
41
- <a href="https://github.com/icanbwell/helix.fhir.client.sdk/releases/latest">
42
- <img src="https://img.shields.io/github/v/release/icanbwell/helix.fhir.client.sdk?display_name=tag"
43
- alt="Latest Release">
44
- </a>
45
- <a href="https://github.com/icanbwell/helix.fhir.client.sdk/blob/main/LICENSE">
46
- <img src="https://img.shields.io/badge/license-Apache%202-blue"
47
- alt="GitHub license">
48
- </a>
49
- </p>
50
-
51
- Fluent API to call the FHIR server that handles:
52
-
53
- 1. Authentication to FHIR server
54
- 2. Renewing access token when they expire
55
- 3. Retry when there are transient errors
56
- 4. Un-bundling the resources received from FHIR server
57
- 5. Paging
58
- 6. Streaming
59
- 7. Logging
60
- 8. Simulating a $graph call when the server does not support it
61
-
62
-
63
- # Usage
64
- `pip install helix.fhir.client.sdk`
65
-
66
- # Documentation
67
- https://icanbwell.github.io/helix.fhir.client.sdk/
68
-
69
- # Test Project using this
70
- https://github.com/icanbwell/fhir-server-performance
71
-
72
- # Python Version Support
73
- * 1.x supports python 3.7+
74
- * 2.x supports python 3.10+
75
- * 3.x supports python 3.12+
76
-
77
- # Asynchronous Support
78
- When communicating with FHIR servers, a lot of time is spent waiting for the server to respond.
79
- This is a good use case for using asynchronous programming.
80
- This SDK supports asynchronous programming using the `async` and `await` keywords.
81
-
82
- The return types are Python AsyncGenerators. Python makes it very easy to work with AsyncGenerators.
83
-
84
- For example, if the SDK provides a function like this:
85
- ```python
86
-
87
- async def get_resources(self) -> AsyncGenerator[FhirGetResponse, None]:
88
- ...
89
- ```
90
-
91
- You can iterate over the results as they become available:
92
- ```python
93
- response: Optional[FhirGetResponse]
94
- async for response in client.get_resources():
95
- print(response.resource)
96
- ```
97
-
98
- Or you can get a list of responses (which will return AFTER all the responses are received:
99
- ```python
100
-
101
- responses: List[FhirGetResponse] = [response async for response in client.get_resources()]
102
- ```
103
-
104
- Or you can aggregate the responses into one response (which will return AFTER all the responses are received:
105
- ```python
106
-
107
- response: Optional[FhirGetResponse] = await FhirGetResponse.from_async_generator(client.get_resources())
108
- ```
109
-
110
- # Data Streaming
111
- For FHIR servers that support data streaming (e.g., b.well FHIR server), you can just set the `use_data_streaming` parameter to stream the data as it i received.
112
- The data will be streamed in AsyncGenerators as described above.
113
-
114
- # Storage Compression
115
- The FHIR client SDK natively stores the FHIR resources compressed in memory. This allows use in environments where you are processing large number of FHIR resources.