Skip to content

Commit ba579be

Browse files
authored
Merge pull request #192 from smart-on-fhir/mikix/headers
server: send correct Accept=application/fhir+json HTTP headers
2 parents 9556bd3 + 559de86 commit ba579be

File tree

5 files changed

+75
-15
lines changed

5 files changed

+75
-15
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ patient = Patient.read('2cda5aad-e409-4070-9a15-e1c35c46ed5a', smart.server)
5353
print(patient.birthDate.isostring)
5454
# '1992-07-03'
5555
print(smart.human_name(patient.name[0]))
56-
# 'Mr. Geoffrey Abbott'
56+
# 'Mr. steve Smith'
5757
```
5858
If this is a protected server, you will first have to send your user to the authorization endpoint to log in.
5959
Just call `smart.authorize_url` to obtain the correct URL.
@@ -90,7 +90,7 @@ from fhirclient.models.patient import Patient
9090
smart = server.FHIRServer(None, 'https://r4.smarthealthit.org')
9191
patient = Patient.read('2cda5aad-e409-4070-9a15-e1c35c46ed5a', smart)
9292
print(patient.name[0].given)
93-
# ['Geoffrey']
93+
# ['steve']
9494
```
9595

9696
##### Search Records on Server

docs/Doxyfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ PROJECT_NAME = "SMART on FHIR Python Client"
3838
# could be handy for archiving the generated documentation or if some version
3939
# control system is used.
4040

41-
PROJECT_NUMBER = 4.3.1
41+
PROJECT_NUMBER = 4.3.2
4242

4343
# Using the PROJECT_BRIEF tag one can provide an optional one line description
4444
# for a project that appears at the top of each page and should give viewer a

fhirclient/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
from .server import FHIRServer, FHIRUnauthorizedException, FHIRNotFoundException
33

4-
__version__ = '4.3.1'
4+
__version__ = '4.3.2' # Update docs/Doxyfile too when you bump this
55
__author__ = 'SMART Platforms Team'
66
__license__ = 'APACHE2'
77
__copyright__ = "Copyright 2017 Boston Children's Hospital"

fhirclient/server.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,19 +159,18 @@ def request_json(self, path, nosign=False):
159159
:throws: Exception on HTTP status >= 400
160160
:returns: Decoded JSON response
161161
"""
162-
headers = {'Accept': 'application/json'}
163-
res = self._get(path, headers, nosign)
162+
res = self._get(path, nosign=nosign)
164163

165164
return res.json()
166165

167-
def request_data(self, path, headers={}, nosign=False):
166+
def request_data(self, path, headers=None, nosign=False):
168167
""" Perform a data request data against the server's base with the
169168
given relative path.
170169
"""
171-
res = self._get(path, headers, nosign)
170+
res = self._get(path, headers=headers, nosign=nosign)
172171
return res.content
173172

174-
def _get(self, path, headers={}, nosign=False):
173+
def _get(self, path, headers=None, nosign=False):
175174
""" Issues a GET request.
176175
177176
:returns: The response object
@@ -184,7 +183,8 @@ def _get(self, path, headers={}, nosign=False):
184183
'Accept-Charset': 'UTF-8',
185184
}
186185
# merge in user headers with defaults
187-
header_defaults.update(headers)
186+
if headers:
187+
header_defaults.update(headers)
188188
# use the merged headers in the request
189189
headers = header_defaults
190190
if not nosign and self.auth is not None and self.auth.can_sign_headers():
@@ -251,10 +251,6 @@ def post_as_form(self, url, formdata, auth=None):
251251
:throws: Exception on HTTP status >= 400
252252
:returns: The response object
253253
"""
254-
headers = {
255-
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
256-
'Accept': 'application/json',
257-
}
258254
res = self.session.post(url, data=formdata, auth=auth)
259255
self.raise_for_status(res)
260256
return res
@@ -276,7 +272,7 @@ def delete_json(self, path, nosign=False):
276272
headers = self.auth.signed_headers(headers)
277273

278274
# perform the request but intercept 401 responses, raising our own Exception
279-
res = self.session.delete(url)
275+
res = self.session.delete(url, headers=headers)
280276
self.raise_for_status(res)
281277
return res
282278

tests/server_test.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import tempfile
66
import unittest
77

8+
import requests
9+
import responses
10+
811
from fhirclient import server
912

1013

@@ -17,6 +20,24 @@ def copy_metadata(filename: str, tmpdir: str) -> None:
1720
os.path.join(tmpdir, 'metadata')
1821
)
1922

23+
@staticmethod
24+
def create_server() -> server.FHIRServer:
25+
return server.FHIRServer(None, state={
26+
'base_uri': "https://example.invalid/",
27+
"auth_type": "oauth2",
28+
"auth": {
29+
"aud": "https://example.invalid/",
30+
"registration_uri": "https://example.invalid/o2/registration",
31+
"authorize_uri": "https://example.invalid/o2/authorize",
32+
"redirect_uri": "https://example.invalid/o2/redirect",
33+
"token_uri": "https://example.invalid/o2/token",
34+
"auth_state": "931f4c31-73e2-4c04-bf6b-b7c9800312ea",
35+
"app_secret": "my-secret",
36+
"access_token": "my-access-token",
37+
"refresh_token": "my-refresh-token",
38+
},
39+
})
40+
2041
def testValidCapabilityStatement(self):
2142
with tempfile.TemporaryDirectory() as tmpdir:
2243
self.copy_metadata('test_metadata_valid.json', tmpdir)
@@ -59,6 +80,49 @@ def testInvalidCapabilityStatement(self):
5980
self.assertEqual("Superfluous entry \"systems\"", str(e.errors[2].errors[1].errors[0].errors[0].errors[0])[:27])
6081
self.assertEqual("Superfluous entry \"formats\"", str(e.errors[3])[:27])
6182

83+
@responses.activate
84+
def testRequestJson(self):
85+
fhir = self.create_server()
86+
fhir.prepare()
87+
88+
bin1 = {"resourceType": "Binary", "id": "bin1"}
89+
mock = responses.add("GET", f"{fhir.base_uri}Binary/bin1", json=bin1)
90+
91+
resp = fhir.request_json("Binary/bin1")
92+
self.assertEqual(resp, bin1)
93+
self.assertEqual(mock.calls[0].request.headers["Accept"], "application/fhir+json")
94+
self.assertEqual(mock.calls[0].request.headers["Accept-Charset"], "UTF-8")
95+
self.assertEqual(mock.calls[0].request.headers["Authorization"], "Bearer my-access-token")
96+
97+
resp = fhir.request_json("Binary/bin1", nosign=True)
98+
self.assertEqual(resp, bin1)
99+
self.assertEqual(mock.calls[1].request.headers["Accept"], "application/fhir+json")
100+
self.assertEqual(mock.calls[1].request.headers["Accept-Charset"], "UTF-8")
101+
self.assertNotIn("Authorization", mock.calls[1].request.headers)
102+
103+
self.assertEqual(mock.call_count, 2)
104+
105+
@responses.activate
106+
def testDeleteJson(self):
107+
fhir = self.create_server()
108+
fhir.prepare()
109+
110+
mock = responses.add("DELETE", f"{fhir.base_uri}Binary/bin1")
111+
112+
resp = fhir.delete_json("Binary/bin1")
113+
self.assertIsInstance(resp, requests.Response)
114+
self.assertEqual(mock.calls[0].request.headers["Accept"], "application/fhir+json")
115+
self.assertEqual(mock.calls[0].request.headers["Accept-Charset"], "UTF-8")
116+
self.assertEqual(mock.calls[0].request.headers["Authorization"], "Bearer my-access-token")
117+
118+
resp = fhir.delete_json("Binary/bin1", nosign=True)
119+
self.assertIsInstance(resp, requests.Response)
120+
self.assertEqual(mock.calls[1].request.headers["Accept"], "application/fhir+json")
121+
self.assertEqual(mock.calls[1].request.headers["Accept-Charset"], "UTF-8")
122+
self.assertNotIn("Authorization", mock.calls[1].request.headers)
123+
124+
self.assertEqual(mock.call_count, 2)
125+
62126

63127
class MockServer(server.FHIRServer):
64128
""" Reads local files.

0 commit comments

Comments
 (0)