Skip to content

Commit

Permalink
continue
Browse files Browse the repository at this point in the history
  • Loading branch information
jnussbaum committed Mar 22, 2024
1 parent 25e8d67 commit 1eb3f23
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 77 deletions.
10 changes: 6 additions & 4 deletions dsp_permissions_scripts/models/api_error.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import pprint
from dataclasses import dataclass, field
from typing import Any
from dataclasses import dataclass


@dataclass(frozen=True)
@dataclass
class ApiError(Exception):
"""Exception raised when an error occurs while calling DSP-API."""

message: str
response_text: str | None = None
status_code: int | None = None
payload: dict[str, Any] = field(default_factory=dict)

def __str__(self) -> str:
return pprint.pformat(vars(self))

@dataclass
class PermissionsAlreadyUpToDate(Exception):
message: str = "The submitted permissions are the same as the current ones"
63 changes: 13 additions & 50 deletions dsp_permissions_scripts/oap/oap_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@
from datetime import datetime
from typing import Any

import requests

from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.models.scope import PermissionScope
from dsp_permissions_scripts.models.value import ValueUpdate
from dsp_permissions_scripts.oap.oap_get import get_resource
from dsp_permissions_scripts.oap.oap_model import Oap
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
from dsp_permissions_scripts.utils import connection

logger = get_logger(__name__)

Expand Down Expand Up @@ -44,8 +41,6 @@ def _update_permissions_for_value(
resource_type: str,
context: dict[str, str],
scope: PermissionScope,
host: str,
token: str,
) -> None:
"""Updates the permissions for the given value (of a property) on a DSP server"""
payload = {
Expand All @@ -58,27 +53,12 @@ def _update_permissions_for_value(
},
"@context": context,
}
protocol = get_protocol(host)
url = f"{protocol}://{host}/v2/values"
headers = {"Authorization": f"Bearer {token}"}
response = http_call_with_retry(
action=lambda: requests.put(url, headers=headers, json=payload, timeout=20),
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:
msg = f"Permissions of resource {resource_iri}, value {value.value_iri} are already up to date"
logger.warning(msg)
elif response.status_code != 200:
raise ApiError(
message=f"Error while updating permissions of resource {resource_iri}, value {value.value_iri}",
response_text=response.text,
status_code=response.status_code,
payload=payload
)
else:
logger.info(f"Updated permissions of resource {resource_iri}, value {value.value_iri}")
try:
connection.con.put("/v2/values", data=payload)
except ApiError as err:
err.message = f"Error while updating permissions of resource {resource_iri}, value {value.value_iri}"
raise err from None
logger.info(f"Updated permissions of resource {resource_iri}, value {value.value_iri}")


def _update_permissions_for_resource(
Expand All @@ -88,7 +68,6 @@ def _update_permissions_for_resource(
context: dict[str, str],
scope: PermissionScope,
host: str,
token: str,
) -> None:
"""Updates the permissions for the given resource on a DSP server"""
payload = {
Expand All @@ -99,28 +78,18 @@ def _update_permissions_for_resource(
}
if lmd:
payload["knora-api:lastModificationDate"] = lmd
protocol = get_protocol(host)
url = f"{protocol}://{host}/v2/resources"
headers = {"Authorization": f"Bearer {token}"}
response = http_call_with_retry(
action=lambda: requests.put(url, headers=headers, json=payload, timeout=20),
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}",
response_text=response.text,
status_code=response.status_code,
payload=payload,
)
try:
connection.con.put("/v2/resources", data=payload)
except ApiError as err:
err.message = f"ERROR while updating permissions of resource {resource_iri}"
raise err from None
logger.info(f"Updated permissions of resource {resource_iri}")


def _update_permissions_for_resource_and_values(
resource_iri: str,
scope: PermissionScope,
host: str,
token: str,
) -> tuple[str, bool]:
"""Updates the permissions for the given resource and its values on a DSP server"""
try:
Expand All @@ -140,7 +109,6 @@ def _update_permissions_for_resource_and_values(
context=resource["@context"],
scope=scope,
host=host,
token=token,
)
except ApiError as err:
logger.error(err)
Expand All @@ -155,8 +123,6 @@ def _update_permissions_for_resource_and_values(
resource_type=resource["@type"],
context=resource["@context"],
scope=scope,
host=host,
token=token,
)
except ApiError as err:
logger.error(err)
Expand All @@ -180,7 +146,6 @@ def _write_failed_res_iris_to_file(
def _launch_thread_pool(
resource_oaps: list[Oap],
host: str,
token: str,
nthreads: int,
) -> list[str]:
counter = 0
Expand All @@ -193,7 +158,6 @@ def _launch_thread_pool(
resource_oap.object_iri,
resource_oap.scope,
host,
token,
) for resource_oap in resource_oaps
]
for result in as_completed(jobs):
Expand All @@ -212,7 +176,6 @@ def _launch_thread_pool(
def apply_updated_oaps_on_server(
resource_oaps: list[Oap],
host: str,
token: str,
shortcode: str,
nthreads: int = 4,
) -> None:
Expand All @@ -227,7 +190,7 @@ def apply_updated_oaps_on_server(
logger.info(f"******* Updating OAPs of {len(resource_oaps)} resources on {host} *******")
print(f"******* Updating OAPs of {len(resource_oaps)} resources on {host} *******")

failed_res_iris = _launch_thread_pool(resource_oaps, host, token, nthreads)
failed_res_iris = _launch_thread_pool(resource_oaps, host, nthreads)

if failed_res_iris:
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
Expand Down
5 changes: 0 additions & 5 deletions dsp_permissions_scripts/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ def update_aps(
def update_doaps(
host: str,
shortcode: str,
token: str,
) -> None:
"""Sample function to modify the Default Object Access Permissions of a project."""
project_doaps = get_doaps_of_project(
Expand Down Expand Up @@ -124,7 +123,6 @@ def update_doaps(
def update_oaps(
host: str,
shortcode: str,
token: str,
) -> None:
"""Sample function to modify the Object Access Permissions of a project."""
resource_oaps = get_all_resource_oaps_of_project(shortcode)
Expand All @@ -137,7 +135,6 @@ def update_oaps(
apply_updated_oaps_on_server(
resource_oaps=resource_oaps_modified,
host=host,
token=token,
shortcode=shortcode,
nthreads=4,
)
Expand Down Expand Up @@ -171,12 +168,10 @@ def main() -> None:
update_doaps(
host=host,
shortcode=shortcode,
token=token,
)
update_oaps(
host=host,
shortcode=shortcode,
token=token,
)


Expand Down
36 changes: 18 additions & 18 deletions dsp_permissions_scripts/utils/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from requests import Response
from requests import Session

from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.models.api_error import ApiError, PermissionsAlreadyUpToDate
from dsp_permissions_scripts.utils.get_logger import get_logger

logger = get_logger(__name__)
Expand Down Expand Up @@ -80,7 +80,7 @@ def login(self, email: str, password: str) -> None:
password: password of the user
Raises:
UserError: if DSP-API returns no token with the provided user credentials
ApiError: if DSP-API returns no token with the provided user credentials
"""
response = self.post(
route="/v2/authentication",
Expand Down Expand Up @@ -119,7 +119,7 @@ def post(
response from server
Raises:
PermanentConnectionError: if the server returns a permanent error
ApiError: if the server returns a permanent error
"""
if data:
headers = headers or {}
Expand All @@ -145,7 +145,7 @@ def get(
response from server
Raises:
PermanentConnectionError: if the server returns a permanent error
ApiError: if the server returns a permanent error
"""
params = RequestParameters("GET", self._make_url(route), self.timeout, headers=headers)
response = self._try_network_action(params)
Expand All @@ -169,7 +169,8 @@ def put(
response from server
Raises:
PermanentConnectionError: if the server returns a permanent error
ApiError: if the server returns a permanent error
PermissionsAlreadyUpToDate: if the permissions are already up to date
"""
if data:
headers = headers or {}
Expand All @@ -195,7 +196,7 @@ def delete(
response from server
Raises:
PermanentConnectionError: if the server returns a permanent error
ApiError: if the server returns a permanent error
"""
params = RequestParameters("DELETE", self._make_url(route), self.timeout, headers=headers)
response = self._try_network_action(params)
Expand All @@ -218,8 +219,8 @@ def _try_network_action(self, params: RequestParameters) -> Response:
params: keyword arguments for the HTTP request
Raises:
BadCredentialsError: if the server returns a 401 status code on the route /v2/authentication
PermanentConnectionError: if the server returns a permanent error
ApiError: if the server returns a permanent error
PermissionsAlreadyUpToDate: if the permissions are already up to date
unexpected exceptions: if the action fails with an unexpected exception
Returns:
Expand All @@ -230,8 +231,8 @@ def _try_network_action(self, params: RequestParameters) -> Response:
try:
self._log_request(params)
response = action()
except (TimeoutError, ReadTimeout) as err:
self._log_and_raise_timeouts(err)
except (TimeoutError, ReadTimeout):
self._log_and_sleep(reason="Timeout Error raised", retry_counter=i, exc_info=True)
except (ConnectionError, RequestException):
self._renew_session()
self._log_and_sleep(reason="Connection Error raised", retry_counter=i, exc_info=True)
Expand All @@ -253,8 +254,13 @@ def _handle_non_ok_responses(self, response: Response, retry_counter: int) -> No
if should_retry:
self._log_and_sleep("Transient Error", retry_counter, exc_info=False)
return None
else:
raise ApiError("Permanently unable to execute the network action", response.text, response.status_code)

already = "dsp.errors.BadRequestException: The submitted permissions are the same as the current ones"
should_break = response.status_code == 400 and response.text and already in response.text
if should_break:
raise PermissionsAlreadyUpToDate()

raise ApiError("Permanently unable to execute the network action", response.text, response.status_code)

def _renew_session(self) -> None:
self.session.close()
Expand All @@ -269,12 +275,6 @@ def _log_and_sleep(self, reason: str, retry_counter: int, exc_info: bool) -> Non
logger.error(f"{msg} ({retry_counter=:})", exc_info=exc_info)
time.sleep(2**retry_counter)

def _log_and_raise_timeouts(self, error: TimeoutError | ReadTimeout) -> None:
msg = f"A '{error.__class__.__name__}' occurred during the connection to the DSP server."
print(f"{datetime.now()}: {msg}")
logger.exception(msg)
raise ApiError(msg) from None

def _log_response(self, response: Response) -> None:
dumpobj: dict[str, Any] = {
"status_code": response.status_code,
Expand Down

0 comments on commit 1eb3f23

Please sign in to comment.