Skip to content

Commit b346c1c

Browse files
Client uses content-type from response to format output
1 parent 1d8a85a commit b346c1c

File tree

4 files changed

+103
-19
lines changed

4 files changed

+103
-19
lines changed

src/daq_config_server/app.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,14 @@ def get_configuration(
5151
match accept:
5252
case AcceptedFileTypes.JSON:
5353
with file_path.open("r", encoding="utf-8") as f:
54-
content = json.load(f)
55-
json_response = JSONResponse(
54+
content = json.loads(f.read())
55+
return JSONResponse(
5656
content=content,
5757
)
58-
return json_response
5958
case AcceptedFileTypes.PLAIN_TEXT:
6059
with file_path.open("r", encoding="utf-8") as f:
6160
content = f.read()
62-
Response(content=content, media_type=accept)
61+
return Response(content=content, media_type=accept)
6362
except Exception as e:
6463
LOGGER.warning(
6564
f"Failed to convert {file_name} to {accept} and caught \

src/daq_config_server/client.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1+
import json
2+
from enum import StrEnum
13
from logging import Logger, getLogger
24
from typing import Any, TypeVar
35

46
import requests
57

8+
from daq_config_server.app import AcceptedFileTypes
9+
610
from .constants import ENDPOINTS
711

812
T = TypeVar("T")
913
BlParamDType = str | int | float | bool
1014

1115

16+
class RequestedResponseFormats(StrEnum):
17+
DICT = AcceptedFileTypes.JSON # Tries to convert to dict using json.loads()
18+
DECODED_STRING = AcceptedFileTypes.PLAIN_TEXT # Use utf-8 decoding in response
19+
RAW_BYTE_STRING = AcceptedFileTypes.RAW_BYTES # Use raw bytes in response
20+
21+
1222
class ConfigServer:
1323
def __init__(self, url: str, log: Logger | None = None) -> None:
1424
self._url = url.rstrip("/")
@@ -17,13 +27,35 @@ def __init__(self, url: str, log: Logger | None = None) -> None:
1727
def _get(
1828
self,
1929
endpoint: str,
30+
headers: dict,
2031
item: str | None = None,
2132
):
22-
r = requests.get(self._url + endpoint + (f"/{item}" if item else ""))
23-
return r.json()
24-
25-
def read_unformatted_file(self, file_path: str) -> Any:
26-
# After https://github.com/DiamondLightSource/daq-config-server/issues/67, we
27-
# can get specific formats, and then have better typing on
28-
# return values
29-
return self._get(ENDPOINTS.CONFIG, file_path)
33+
r = requests.get(
34+
self._url + endpoint + (f"/{item}" if item else ""), headers=headers
35+
)
36+
37+
content_type = r.headers["content-type"].split(";")[0].strip()
38+
39+
try:
40+
match content_type:
41+
case AcceptedFileTypes.JSON:
42+
content = json.loads(r.content)
43+
case AcceptedFileTypes.PLAIN_TEXT:
44+
content = r.text
45+
case _:
46+
content = r.content
47+
except Exception:
48+
# TODO warn here
49+
content = r.content
50+
51+
return content
52+
53+
def get_file_contents(
54+
self,
55+
file_path: str,
56+
requested_response_format: RequestedResponseFormats = (
57+
RequestedResponseFormats.DECODED_STRING
58+
),
59+
) -> Any:
60+
headers = {"Accept": requested_response_format}
61+
return self._get(ENDPOINTS.CONFIG, headers, file_path)

tests/system_tests/test_client.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import json
2+
13
import pytest
24

3-
from daq_config_server.client import ConfigServer
5+
from daq_config_server.client import ConfigServer, RequestedResponseFormats
46
from tests.constants import TEST_DATA_DIR
57

68
SERVER_ADDRESS = "http://0.0.0.0:8555"
@@ -21,11 +23,59 @@ def server():
2123

2224

2325
@pytest.mark.requires_local_server
24-
def test_read_unformatted_file(server: ConfigServer):
26+
def test_read_unformatted_file_as_plain_text(server: ConfigServer):
2527
file_path = f"{TEST_DATA_DIR}/beamline_parameters.txt"
2628
with open(file_path) as f:
2729
expected_response = f.read()
30+
31+
assert (
32+
server.get_file_contents(
33+
file_path,
34+
)
35+
== expected_response
36+
)
37+
38+
39+
@pytest.mark.requires_local_server
40+
def test_read_file_as_bytes(server: ConfigServer):
41+
file_path = f"{TEST_DATA_DIR}/beamline_parameters.txt"
42+
with open(file_path, "rb") as f:
43+
expected_response = f.read()
44+
45+
assert (
46+
server.get_file_contents(
47+
file_path,
48+
requested_response_format=RequestedResponseFormats.RAW_BYTE_STRING,
49+
)
50+
== expected_response
51+
)
52+
53+
54+
@pytest.mark.requires_local_server
55+
def test_read_good_json_as_dict(server: ConfigServer):
56+
file_path = f"{TEST_DATA_DIR}/test_good_json.json"
57+
with open(file_path) as f:
58+
expected_response = json.loads(f.read())
59+
60+
assert (
61+
server.get_file_contents(
62+
file_path,
63+
requested_response_format=RequestedResponseFormats.DICT,
64+
)
65+
== expected_response
66+
)
67+
68+
69+
@pytest.mark.requires_local_server
70+
def test_bad_json_read_as_bytes(server: ConfigServer):
71+
file_path = f"{TEST_DATA_DIR}/test_bad_json"
72+
with open(file_path, "rb") as f:
73+
expected_response = f.read()
74+
2875
assert (
29-
server.read_unformatted_file(f"{TEST_DATA_DIR}/beamline_parameters.txt")
76+
server.get_file_contents(
77+
file_path,
78+
requested_response_format=RequestedResponseFormats.DICT,
79+
)
3080
== expected_response
3181
)

tests/unit_tests/test_client.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@
33
from fastapi import status
44
from httpx import Response
55

6-
from daq_config_server.client import ConfigServer
6+
from daq_config_server.client import ConfigServer, RequestedResponseFormats
77
from daq_config_server.constants import ENDPOINTS
88

99

1010
# More useful tests for the client are in tests/system_tests
1111
@patch("daq_config_server.client.requests.get")
12-
def test_read_unformatted_file(mock_request: MagicMock):
12+
def test_get_file_contents(mock_request: MagicMock):
1313
mock_request.return_value = Response(status_code=status.HTTP_200_OK, json="test")
1414
file_path = "test"
1515
url = "url"
1616
server = ConfigServer(url)
17-
server.read_unformatted_file(file_path)
18-
mock_request.assert_called_once_with(url + ENDPOINTS.CONFIG + "/" + file_path)
17+
server.get_file_contents(file_path)
18+
mock_request.assert_called_once_with(
19+
url + ENDPOINTS.CONFIG + "/" + file_path,
20+
headers={"Accept": RequestedResponseFormats.DECODED_STRING},
21+
)

0 commit comments

Comments
 (0)