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

chore: improve error handling #48

Merged
merged 5 commits into from
Oct 15, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dsp_permissions_scripts/ap/ap_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import requests

from dsp_permissions_scripts.ap.ap_model import Ap, ApValue
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.project import get_project_iri_by_shortcode
Expand Down Expand Up @@ -47,7 +48,8 @@ def _get_all_aps_of_project(
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/ap/{project_iri}"
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not get APs of project", 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
18 changes: 13 additions & 5 deletions dsp_permissions_scripts/ap/ap_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
create_ap_from_admin_route_object,
)
from dsp_permissions_scripts.ap.ap_model import Ap
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, get_timestamp

Expand All @@ -34,7 +35,8 @@ def _delete_single_ap(
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/{ap_iri}"
response = requests.delete(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError(f"Could not delete Administrative Permission {ap.iri}", response.text, response.status_code)
logger.info(f"Deleted Administrative Permission {ap.iri} on host {host}")


Expand All @@ -52,7 +54,13 @@ def _update_ap(
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=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError(
message=f"Could not update Administrative Permission {ap.iri}",
response_text=response.text,
status_code=response.status_code,
payload=payload,
)
ap_updated: dict[str, Any] = response.json()["administrative_permission"]
ap_object_updated = create_ap_from_admin_route_object(ap_updated)
return ap_object_updated
Expand Down Expand Up @@ -90,9 +98,9 @@ def apply_updated_aps_on_server(
token=token,
)
_log_and_print_ap_update(ap=new_ap)
except Exception: # pylint: disable=broad-exception-caught
logger.error(f"ERROR while updating Administrative Permission {ap.iri}", exc_info=True)
warnings.warn(f"ERROR while updating Administrative Permission {ap.iri}")
except ApiError as err:
logger.error(err)
warnings.warn(err.message)

print(f"{get_timestamp()}: All APs have been updated.")

Expand Down
4 changes: 3 additions & 1 deletion dsp_permissions_scripts/doap/doap_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import requests

from dsp_permissions_scripts.doap.doap_model import Doap, DoapTarget, DoapTargetType
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.project import get_project_iri_by_shortcode
Expand Down Expand Up @@ -47,7 +48,8 @@ def _get_all_doaps_of_project(
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/doap/{project_iri}"
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
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"]
doap_objects = [create_doap_from_admin_route_response(doap) for doap in doaps]
return doap_objects
Expand Down
10 changes: 6 additions & 4 deletions dsp_permissions_scripts/doap/doap_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from dsp_permissions_scripts.doap.doap_get import create_doap_from_admin_route_response
from dsp_permissions_scripts.doap.doap_model import Doap
from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.models.scope import PermissionScope
from dsp_permissions_scripts.utils.authentication import get_protocol
from dsp_permissions_scripts.utils.get_logger import get_logger, get_timestamp
Expand All @@ -30,7 +31,8 @@ def _update_doap_scope(
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=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
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"])
return new_doap

Expand Down Expand Up @@ -68,8 +70,8 @@ def apply_updated_doaps_on_server(
token=token,
)
_log_and_print_doap_update(doap=new_doap)
except Exception: # pylint: disable=broad-exception-caught
logger.error(f"ERROR while updating DOAP {d.doap_iri}", exc_info=True)
warnings.warn(f"ERROR while updating DOAP {d.doap_iri}")
except ApiError as err:
logger.error(err)
warnings.warn(err.message)

print(f"{get_timestamp()}: All DOAPs have been updated.")
16 changes: 16 additions & 0 deletions dsp_permissions_scripts/models/api_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pprint
from dataclasses import dataclass, field
from typing import Any


@dataclass(frozen=True)
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))
jnussbaum marked this conversation as resolved.
Show resolved Hide resolved
29 changes: 18 additions & 11 deletions dsp_permissions_scripts/oap/oap_get_set.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# pylint: disable=too-many-arguments

import json
import warnings
from typing import Any
from urllib.parse import quote_plus

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_model import Oap
Expand Down Expand Up @@ -50,7 +50,8 @@ def _get_resource(
url = f"{protocol}://{host}/v2/resources/{iri}"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
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()
return data

Expand Down Expand Up @@ -109,11 +110,11 @@ def _update_permissions_for_value(
msg = f"Permissions of resource {resource_iri}, value {value.value_iri} are already up to date"
logger.warning(msg)
elif response.status_code != 200:
logger.error(
f"Error while updating permissions of resource {resource_iri}, value {value.value_iri}. "
f"Response status code: {response.status_code}. "
f"Response text: {response.text}. "
f"Payload: {json.dumps(payload, indent=4)}"
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}")
Expand Down Expand Up @@ -143,7 +144,13 @@ def _update_permissions_for_resource(
url = f"{protocol}://{host}/v2/resources"
headers = {"Authorization": f"Bearer {token}"}
response = requests.put(url, headers=headers, json=payload, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
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,
)
logger.info(f"Updated permissions of resource {resource_iri}")


Expand Down Expand Up @@ -214,9 +221,9 @@ def apply_updated_oaps_on_server(
host=host,
token=token,
)
except Exception: # pylint: disable=broad-exception-caught
logger.error(f"ERROR while updating permissions of resource {resource_oap.object_iri}", exc_info=True)
warnings.warn(f"ERROR while updating permissions of resource {resource_oap.object_iri}")
except ApiError as err:
logger.error(err)
warnings.warn(err.message)
failed_res_iris.append(resource_oap.object_iri)
logger.info(f"Updated permissions of resource {resource_oap.object_iri} and its values.")

Expand Down
5 changes: 4 additions & 1 deletion dsp_permissions_scripts/utils/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import requests

from dsp_permissions_scripts.models.api_error import ApiError


def _get_token(host: str, email: str, pw: str) -> str:
"""
Expand All @@ -10,7 +12,8 @@ def _get_token(host: str, email: str, pw: str) -> str:
protocol = get_protocol(host)
url = f"{protocol}://{host}/v2/authentication"
response = requests.post(url, json={"email": email, "password": pw}, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not login", response.text, response.status_code)
token: str = response.json()["token"]
return token

Expand Down
37 changes: 24 additions & 13 deletions dsp_permissions_scripts/utils/project.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import warnings
from urllib.parse import quote_plus

import requests

from dsp_permissions_scripts.models.api_error import ApiError
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, get_timestamp
Expand Down Expand Up @@ -43,7 +45,8 @@ def _get_onto_iris_of_project(
url = f"{protocol}://{host}/v2/ontologies/metadata"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not get onto IRIs", response.text, response.status_code)
all_ontologies = response.json().get("@graph")
project_onto_iris = [o["@id"] for o in all_ontologies if o["knora-api:attachedToProject"]["@id"] == project_iri]
return project_onto_iris
Expand All @@ -58,7 +61,8 @@ def _get_class_iris_of_onto(
url = f"{protocol}://{host}/v2/ontologies/allentities/{quote_plus(onto_iri)}"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not get class IRIs", response.text, response.status_code)
all_entities = response.json()["@graph"]
context = response.json()["@context"]
class_ids = [c["@id"] for c in all_entities if c.get("knora-api:isResourceClass")]
Expand All @@ -81,15 +85,20 @@ def _get_all_resource_oaps_of_resclass(
more = True
while more:
logger.info(f"Getting page {page}...")
more, iris = _get_next_page(
protocol=protocol,
host=host,
resclass_iri=resclass_iri,
page=page,
headers=headers,
)
resources.extend(iris)
page += 1
try:
more, iris = _get_next_page(
protocol=protocol,
host=host,
resclass_iri=resclass_iri,
page=page,
headers=headers,
)
resources.extend(iris)
page += 1
except ApiError as err:
logger.error(f"{err}\nStop getting more pages, return what has been retrieved so far.")
warnings.warn(f"{err.message}\nStop getting more pages, return what has been retrieved so far.")
more = False
print(f"{get_timestamp()}: Retrieved {len(resources)} resource OAPs of class {resclass_iri}.")
logger.info(f"Retrieved {len(resources)} resource OAPs of class {resclass_iri}.")
return resources
Expand All @@ -113,7 +122,8 @@ def _get_next_page(
"""
url = f"{protocol}://{host}/v2/resources?resourceClass={quote_plus(resclass_iri)}&page={page}"
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not get next page", response.text, response.status_code)
result = response.json()
if "@graph" in result:
# result contains several resources: return them, then continue with next page
Expand All @@ -138,7 +148,8 @@ 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=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Cannot retrieve project IRI", response.text, response.status_code)
iri: str = response.json()["project"]["id"]
return iri

Expand Down