Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: option for custom headers in HTTP requests (DEV-3145) #702

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/dsp_tools/commands/xmlupload/resource_create_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ResourceCreateClient:
json_ld_context: dict[str, str]
permissions_lookup: dict[str, Permissions]
listnode_lookup: dict[str, str]
media_previously_ingested: bool = False

def create_resource(
self,
Expand All @@ -41,7 +42,8 @@ def create_resource(
)
resource_dict = self._make_resource_with_values(resource, bitstream_information)
resource_json_ld = json.dumps(resource_dict, ensure_ascii=False)
res = self.con.post(route="/v2/resources", jsondata=resource_json_ld)
headers = {"X-Asset-Ingested": "true"} if self.media_previously_ingested else None
res = self.con.post(route="/v2/resources", jsondata=resource_json_ld, headers=headers)
iri = res["@id"]
label = res["rdfs:label"]
return iri, label
Expand Down
8 changes: 2 additions & 6 deletions src/dsp_tools/commands/xmlupload/xmlupload.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,7 @@ def xmlupload(

# establish connection to DSP server
con = login(server=server, user=user, password=password, dump=config.diagnostics.dump)
if config.media_previously_uploaded:
sipi_con = ConnectionLive(
sipi, dump=config.diagnostics.dump, token=con.get_token(), headers={"X-Asset-Ingested": "true"}
)
else:
sipi_con = ConnectionLive(sipi, dump=config.diagnostics.dump, token=con.get_token())
sipi_con = ConnectionLive(sipi, dump=config.diagnostics.dump, token=con.get_token())
sipi_server = Sipi(sipi_con)

ontology_client = OntologyClientLive(
Expand Down Expand Up @@ -339,6 +334,7 @@ def _upload_resources(
json_ld_context=json_ld_context,
permissions_lookup=permissions_lookup,
listnode_lookup=listnode_lookup,
media_previously_ingested=config.media_previously_uploaded,
)

for i, resource in enumerate(resources):
Expand Down
8 changes: 8 additions & 0 deletions src/dsp_tools/utils/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@ class Connection(Protocol):
def get(
self,
route: str,
headers: dict[str, str] | None = None,
) -> dict[str, Any]:
"""
Make a HTTP GET request to the server to which this connection has been established.

Args:
route: route that will be called on the server
headers: headers for the HTTP request
"""

def put(
self,
route: str,
jsondata: str | None = None,
headers: dict[str, str] | None = None,
content_type: str = "application/json",
) -> dict[str, Any]:
"""
Expand All @@ -35,6 +38,7 @@ def put(
Args:
route: route that will be called on the server
jsondata: Valid JSON as string
headers: headers for the HTTP request
content_type: HTTP Content-Type [default: 'application/json']
"""

Expand All @@ -43,6 +47,7 @@ def post(
route: str,
jsondata: str | None = None,
files: dict[str, tuple[str, Any]] | None = None,
headers: dict[str, str] | None = None,
timeout: int | None = None,
) -> dict[str, Any]:
"""
Expand All @@ -52,20 +57,23 @@ def post(
route: route that will be called on the server
jsondata: Valid JSON as string
files: files to be uploaded, if any
headers: headers for the HTTP request
timeout: timeout of the HTTP request, or None if the default should be used
"""

def delete(
self,
route: str,
params: dict[str, Any] | None = None,
headers: dict[str, str] | None = None,
) -> dict[str, Any]:
"""
Make a HTTP GET request to the server to which this connection has been established.

Args:
route: route that will be called on the server
params: additional parameters for the HTTP request
headers: headers for the HTTP request
"""

def get_token(self) -> str:
Expand Down
47 changes: 34 additions & 13 deletions src/dsp_tools/utils/connection_live.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
import time
from dataclasses import dataclass, field
from dataclasses import dataclass
from datetime import datetime
from functools import partial
from pathlib import Path
Expand Down Expand Up @@ -114,7 +114,6 @@ class ConnectionLive:
dump: bool = False
dump_directory = Path("HTTP requests")
token: Optional[str] = None
headers: dict[str, str] = field(default_factory=dict)

def __post_init__(self) -> None:
"""
Expand Down Expand Up @@ -179,6 +178,7 @@ def _write_request_to_file(
jsondata: Optional[str],
params: Optional[dict[str, Any]],
response: requests.Response,
headers: dict[str, str] | None = None,
uploaded_file: Optional[str] = None,
) -> None:
"""
Expand All @@ -190,6 +190,7 @@ def _write_request_to_file(
jsondata: data sent to the server
params: additional parameters for the HTTP request
response: response of the server
headers: headers of the HTTP request
uploaded_file: path to the file that was uploaded, if any
"""
if response.status_code == 200:
Expand All @@ -200,7 +201,7 @@ def _write_request_to_file(
"DSP server": self.server,
"url": url,
"method": method,
"headers": self.headers,
"headers": headers,
"params": params,
"body": json.loads(jsondata) if jsondata else None,
"uploaded file": uploaded_file,
Expand All @@ -217,6 +218,7 @@ def post(
route: str,
jsondata: Optional[str] = None,
files: dict[str, tuple[str, Any]] | None = None,
headers: dict[str, str] | None = None,
timeout: int | None = None,
) -> dict[str, Any]:
"""
Expand All @@ -226,6 +228,7 @@ def post(
route: route that will be called on the server
jsondata: Valid JSON as string
files: files to be uploaded, if any
headers: headers for the HTTP request
timeout: timeout of the HTTP request, or None if the default should be used

Returns:
Expand All @@ -239,12 +242,14 @@ def post(
if not route.startswith("/"):
route = f"/{route}"
url = self.server + route
if not headers:
headers = {}
if jsondata:
self.headers["Content-Type"] = "application/json; charset=UTF-8"
headers["Content-Type"] = "application/json; charset=UTF-8"
if self.token:
self.headers["Authorization"] = f"Bearer {self.token}"
headers["Authorization"] = f"Bearer {self.token}"

request = partial(requests.post, url=url, headers=self.headers, timeout=timeout)
request = partial(requests.post, url=url, headers=headers, timeout=timeout)
if jsondata:
# if data is not encoded as bytes, issues can occur with non-ASCII characters,
# where the content-length of the request will turn out to be different from the actual length
Expand All @@ -262,33 +267,38 @@ def post(
uploaded_file=files["file"][0] if files else None,
params=None,
response=response,
headers=headers,
)
check_for_api_error(response)
return cast(dict[str, Any], response.json())

def get(
self,
route: str,
headers: dict[str, str] | None = None,
) -> dict[str, Any]:
"""
Make a HTTP GET request to the server to which this connection has been established.

Args:
route: route that will be called on the server
headers: headers for the HTTP request

Returns:
response from server
"""
if not route.startswith("/"):
route = f"/{route}"
url = self.server + route
if not headers:
headers = {}
if self.token:
self.headers["Authorization"] = f"Bearer {self.token}"
headers["Authorization"] = f"Bearer {self.token}"

response: Response = _try_network_action(
lambda: requests.get(
url=url,
headers=self.headers,
headers=headers,
timeout=20,
)
)
Expand All @@ -299,6 +309,7 @@ def get(
jsondata=None,
params=None,
response=response,
headers=headers,
)
check_for_api_error(response)
return cast(dict[str, Any], response.json())
Expand All @@ -307,6 +318,7 @@ def put(
self,
route: str,
jsondata: Optional[str] = None,
headers: dict[str, str] | None = None,
content_type: str = "application/json",
) -> dict[str, Any]:
"""
Expand All @@ -315,6 +327,7 @@ def put(
Args:
route: route that will be called on the server
jsondata: Valid JSON as string
headers: headers of the HTTP request
content_type: HTTP Content-Type [default: 'application/json']

Returns:
Expand All @@ -327,15 +340,17 @@ def put(
if not route.startswith("/"):
route = f"/{route}"
url = self.server + route
if not headers:
headers = {}
Nora-Olivia-Ammann marked this conversation as resolved.
Show resolved Hide resolved
if jsondata:
self.headers["Content-Type"] = f"{content_type}; charset=UTF-8"
headers["Content-Type"] = f"{content_type}; charset=UTF-8"
if self.token:
self.headers["Authorization"] = f"Bearer {self.token}"
headers["Authorization"] = f"Bearer {self.token}"

response: Response = _try_network_action(
lambda: requests.put(
url=url,
headers=self.headers,
headers=headers,
# if data is not encoded as bytes, issues can occur with non-ASCII characters,
# where the content-length of the request will turn out to be different from the actual length
data=jsondata.encode("utf-8") if jsondata else None,
Expand All @@ -349,6 +364,7 @@ def put(
jsondata=jsondata,
params=None,
response=response,
headers=headers,
)
check_for_api_error(response)
return cast(dict[str, Any], response.json())
Expand All @@ -357,25 +373,29 @@ def delete(
self,
route: str,
params: Optional[dict[str, Any]] = None,
headers: dict[str, str] | None = None,
) -> dict[str, Any]:
"""
Make a HTTP GET request to the server to which this connection has been established.

Args:
route: route that will be called on the server
params: additional parameters for the HTTP request
headers: headers for the HTTP request

Returns:
response from server
"""
if not route.startswith("/"):
route = f"/{route}"
url = self.server + route
if not headers:
headers = {}
if self.token:
self.headers["Authorization"] = f"Bearer {self.token}"
headers["Authorization"] = f"Bearer {self.token}"
response = requests.delete(
url=url,
headers=self.headers,
headers=headers,
params=params,
timeout=20,
)
Expand All @@ -386,6 +406,7 @@ def delete(
jsondata=None,
params=params,
response=response,
headers=headers,
)
check_for_api_error(response)
return cast(dict[str, Any], response.json())
18 changes: 15 additions & 3 deletions test/unittests/commands/xmlupload/connection_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,37 @@
class ConnectionMockBase:
"""
Base mock for the Connection class.
Does not have any capabilities but subclasses can override methods.
Does not have any capabilities, but subclasses can override methods.
"""

def get(self, route: str, headers: dict[str, str] | None = None) -> dict[str, Any]:
raise AssertionError("GET not implemented in mock")

def put(self, route: str, jsondata: str | None = None, content_type: str = "application/json") -> dict[str, Any]:
def put(
self,
route: str,
jsondata: str | None = None,
headers: dict[str, str] | None = None,
content_type: str = "application/json",
) -> dict[str, Any]:
raise AssertionError("PUT not implemented in mock")

def post(
self,
route: str,
jsondata: str | None = None,
files: dict[str, tuple[str, Any]] | None = None,
headers: dict[str, str] | None = None,
timeout: int | None = None,
) -> dict[str, Any]:
raise AssertionError("POST not implemented in mock")

def delete(self, route: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
def delete(
self,
route: str,
params: dict[str, Any] | None = None,
headers: dict[str, str] | None = None,
) -> dict[str, Any]:
raise AssertionError("DELETE not implemented in mock")

def get_token(self) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def post(
route: str,
jsondata: str | None = None,
files: dict[str, tuple[str, Any]] | None = None,
headers: dict[str, str] | None = None,
timeout: int | None = None,
) -> dict[str, Any]:
return self.post_responses.pop(0)
Expand All @@ -63,6 +64,7 @@ def put(
self,
route: str,
jsondata: str | None = None,
headers: dict[str, str] | None = None,
content_type: str = "application/json",
) -> dict[str, Any]:
return self.put_responses.pop(0)
Expand Down