Skip to content

Commit

Permalink
Merge branch 'main' into wip/scenario-tanner
Browse files Browse the repository at this point in the history
  • Loading branch information
jnussbaum authored Oct 18, 2023
2 parents 03d65b7 + c0830ea commit e614d45
Show file tree
Hide file tree
Showing 9 changed files with 398 additions and 434 deletions.
8 changes: 6 additions & 2 deletions dsp_permissions_scripts/ap/ap_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from dsp_permissions_scripts.utils.authentication import get_protocol
from dsp_permissions_scripts.utils.get_logger import get_logger
from dsp_permissions_scripts.utils.project import get_project_iri_by_shortcode
from dsp_permissions_scripts.utils.try_request import http_call_with_retry

logger = get_logger(__name__)

Expand Down Expand Up @@ -46,9 +47,12 @@ def _get_all_aps_of_project(
project_iri = quote_plus(project_iri, safe="")
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/ap/{project_iri}"
response = requests.get(url, headers=headers, timeout=10)
response = http_call_with_retry(
action=lambda: requests.get(url, headers=headers, timeout=10),
err_msg=f"Could not get APs of project {project_iri}",
)
if response.status_code != 200:
raise ApiError("Could not get APs of project", response.text, response.status_code)
raise ApiError(f"Could not get APs of project {project_iri}", response.text, response.status_code)
aps: list[dict[str, Any]] = response.json()["administrative_permissions"]
ap_objects = [create_ap_from_admin_route_object(ap) for ap in aps]
return ap_objects
Expand Down
11 changes: 9 additions & 2 deletions dsp_permissions_scripts/ap/ap_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.utils.authentication import get_protocol
from dsp_permissions_scripts.utils.get_logger import get_logger
from dsp_permissions_scripts.utils.try_request import http_call_with_retry

logger = get_logger(__name__)

Expand All @@ -25,7 +26,10 @@ def _delete_ap_on_server(
ap_iri = quote_plus(ap.iri, safe="")
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/{ap_iri}"
response = requests.delete(url, headers=headers, timeout=10)
response = http_call_with_retry(
action=lambda: requests.delete(url, headers=headers, timeout=10),
err_msg=f"Could not delete Administrative Permission {ap.iri}",
)
if response.status_code != 200:
raise ApiError(f"Could not delete Administrative Permission {ap.iri}", response.text, response.status_code)

Expand All @@ -40,7 +44,10 @@ def _update_ap_on_server(
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/{iri}/hasPermissions"
payload = {"hasPermissions": create_admin_route_object_from_ap(ap)["hasPermissions"]}
response = requests.put(url, headers=headers, json=payload, timeout=10)
response = http_call_with_retry(
action=lambda: requests.put(url, headers=headers, json=payload, timeout=10),
err_msg=f"Could not update Administrative Permission {ap.iri}",
)
if response.status_code != 200:
raise ApiError(
message=f"Could not update Administrative Permission {ap.iri}",
Expand Down
6 changes: 5 additions & 1 deletion dsp_permissions_scripts/doap/doap_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from dsp_permissions_scripts.utils.scope_serialization import (
create_scope_from_admin_route_object,
)
from dsp_permissions_scripts.utils.try_request import http_call_with_retry

logger = get_logger(__name__)

Expand Down Expand Up @@ -44,7 +45,10 @@ def _get_all_doaps_of_project(
project_iri = quote_plus(project_iri, safe="")
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/doap/{project_iri}"
response = requests.get(url, headers=headers, timeout=10)
response = http_call_with_retry(
action=lambda: requests.get(url, headers=headers, timeout=10),
err_msg=f"Error while getting DOAPs of project {project_iri}",
)
if response.status_code != 200:
raise ApiError(f"Error while getting DOAPs of project {project_iri}", response.text, response.status_code)
doaps: list[dict[str, Any]] = response.json()["default_object_access_permissions"]
Expand Down
6 changes: 5 additions & 1 deletion dsp_permissions_scripts/doap/doap_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from dsp_permissions_scripts.utils.scope_serialization import (
create_admin_route_object_from_scope,
)
from dsp_permissions_scripts.utils.try_request import http_call_with_retry

logger = get_logger(__name__)

Expand All @@ -27,7 +28,10 @@ def _update_doap_scope_on_server(
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/{iri}/hasPermissions"
payload = {"hasPermissions": create_admin_route_object_from_scope(scope)}
response = requests.put(url, headers=headers, json=payload, timeout=10)
response = http_call_with_retry(
action=lambda: requests.put(url, headers=headers, json=payload, timeout=10),
err_msg=f"Could not update scope of DOAP {doap_iri}",
)
if response.status_code != 200:
raise ApiError( f"Could not update scope of DOAP {doap_iri}", response.text, response.status_code, payload)
new_doap = create_doap_from_admin_route_response(response.json()["default_object_access_permission"])
Expand Down
16 changes: 13 additions & 3 deletions dsp_permissions_scripts/oap/oap_get_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from dsp_permissions_scripts.utils.authentication import get_protocol
from dsp_permissions_scripts.utils.get_logger import get_logger
from dsp_permissions_scripts.utils.scope_serialization import create_string_from_scope
from dsp_permissions_scripts.utils.try_request import http_call_with_retry

logger = get_logger(__name__)

Expand Down Expand Up @@ -45,7 +46,10 @@ def _get_resource(
protocol = get_protocol(host)
url = f"{protocol}://{host}/v2/resources/{iri}"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, timeout=10)
response = http_call_with_retry(
action=lambda: requests.get(url, headers=headers, timeout=10),
err_msg=f"Error while getting resource {resource_iri}",
)
if response.status_code != 200:
raise ApiError( f"Error while getting resource {resource_iri}", response.text, response.status_code)
data: dict[str, Any] = response.json()
Expand Down Expand Up @@ -75,7 +79,10 @@ def _update_permissions_for_value(
protocol = get_protocol(host)
url = f"{protocol}://{host}/v2/values"
headers = {"Authorization": f"Bearer {token}"}
response = requests.put(url, headers=headers, json=payload, timeout=10)
response = http_call_with_retry(
action=lambda: requests.put(url, headers=headers, json=payload, timeout=10),
err_msg=f"Error while updating permissions of resource {resource_iri}, value {value.value_iri}",
)
if response.status_code == 400 and response.text:
already = "dsp.errors.BadRequestException: The submitted permissions are the same as the current ones"
if already in response.text:
Expand Down Expand Up @@ -113,7 +120,10 @@ def _update_permissions_for_resource(
protocol = get_protocol(host)
url = f"{protocol}://{host}/v2/resources"
headers = {"Authorization": f"Bearer {token}"}
response = requests.put(url, headers=headers, json=payload, timeout=10)
response = http_call_with_retry(
action=lambda: requests.put(url, headers=headers, json=payload, timeout=10),
err_msg=f"ERROR while updating permissions of resource {resource_iri}",
)
if response.status_code != 200:
raise ApiError(
message=f"ERROR while updating permissions of resource {resource_iri}",
Expand Down
21 changes: 17 additions & 4 deletions dsp_permissions_scripts/utils/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from dsp_permissions_scripts.utils.get_logger import get_logger
from dsp_permissions_scripts.utils.helpers import dereference_prefix
from dsp_permissions_scripts.utils.scope_serialization import create_scope_from_string
from dsp_permissions_scripts.utils.try_request import http_call_with_retry

logger = get_logger(__name__)

Expand Down Expand Up @@ -45,7 +46,10 @@ def _get_onto_iris_of_project(
protocol = get_protocol(host)
url = f"{protocol}://{host}/v2/ontologies/metadata"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, timeout=10)
response = http_call_with_retry(
action=lambda: requests.get(url, headers=headers, timeout=10),
err_msg="Could not get onto IRIs",
)
if response.status_code != 200:
raise ApiError("Could not get onto IRIs", response.text, response.status_code)
all_ontologies = response.json().get("@graph")
Expand All @@ -61,7 +65,10 @@ def _get_class_iris_of_onto(
protocol = get_protocol(host)
url = f"{protocol}://{host}/v2/ontologies/allentities/{quote_plus(onto_iri)}"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, timeout=10)
response = http_call_with_retry(
action=lambda: requests.get(url, headers=headers, timeout=10),
err_msg="Could not get class IRIs",
)
if response.status_code != 200:
raise ApiError("Could not get class IRIs", response.text, response.status_code)
all_entities = response.json()["@graph"]
Expand Down Expand Up @@ -121,7 +128,10 @@ def _get_next_page(
This means that the page must be incremented until the response contains 0 or 1 resource.
"""
url = f"{protocol}://{host}/v2/resources?resourceClass={quote_plus(resclass_iri)}&page={page}"
response = requests.get(url, headers=headers, timeout=20)
response = http_call_with_retry(
action=lambda: requests.get(url, headers=headers, timeout=20),
err_msg="Could not get next page",
)
if response.status_code != 200:
raise ApiError("Could not get next page", response.text, response.status_code)
result = response.json()
Expand All @@ -144,7 +154,10 @@ def _get_next_page(
def get_project_iri_by_shortcode(shortcode: str, host: str) -> str:
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/projects/shortcode/{shortcode}"
response = requests.get(url, timeout=10)
response = http_call_with_retry(
action=lambda: requests.get(url, timeout=10),
err_msg="Cannot retrieve project IRI",
)
if response.status_code != 200:
raise ApiError("Cannot retrieve project IRI", response.text, response.status_code)
iri: str = response.json()["project"]["id"]
Expand Down
52 changes: 52 additions & 0 deletions dsp_permissions_scripts/utils/try_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import time
from typing import Callable

import requests
from requests import ReadTimeout, RequestException
from urllib3.exceptions import ReadTimeoutError

from dsp_permissions_scripts.utils.get_logger import get_logger, get_timestamp

logger = get_logger(__name__)


def http_call_with_retry(action: Callable[..., requests.Response], err_msg: str) -> requests.Response:
"""
Function that tries 9 times to execute an HTTP request.
404 and 500-599 codes are catched, together with some errors raised by the requests library,
and the request is retried after a waiting time.
The waiting times are 1, 2, 4, 8, 16, 32, 64, 128, 256 seconds.
If it still fails, the request is executed without any catch, so that the error escalates to the caller.
Use this only for actions that can be retried without side effects.
Args:
action: one of requests.get(), requests.post(), requests.put(), requests.delete()
err_msg: this message is printed and logged when there is a problem with the call
Raises:
errors from the requests library
Returns:
response of the HTTP request
"""
for i in range(9):
try:
response: requests.Response = action()
if response.status_code == 200:
return response
retry_code = 500 <= response.status_code < 600 or response.status_code == 404
try_again_later = "try again later" in response.text
if retry_code or try_again_later:
msg = f"{err_msg}. Retry request in {2 ** i} seconds... ({response.status_code}: {response.text})"
print(f"{get_timestamp()}: SERVER ERROR: {msg}")
logger.error(msg)
continue
return response
except (TimeoutError, ReadTimeout, ReadTimeoutError, RequestException, ConnectionError):
print(f"{get_timestamp()}: Server Error: Retry request in {2 ** i} seconds...")
logger.error(f"Server Error: Retry request in {2 ** i} seconds...", exc_info=True)
time.sleep(2**i)
continue

logger.error("Permanently unable to execute the API call. See logs for more details.")
return action()
Loading

0 comments on commit e614d45

Please sign in to comment.