Skip to content

Commit

Permalink
refactor: introduce abstraction layer for connection (DEV-2820) (#570)
Browse files Browse the repository at this point in the history
Co-authored-by: Johannes Nussbaum <39048939+jnussbaum@users.noreply.github.com>
  • Loading branch information
BalduinLandolt and jnussbaum committed Oct 17, 2023
1 parent 863c631 commit 87c1e18
Show file tree
Hide file tree
Showing 32 changed files with 217 additions and 185 deletions.
Empty file.
94 changes: 94 additions & 0 deletions src/dsp_tools/connection/connection.py
@@ -0,0 +1,94 @@
from typing import Any, Protocol


class Connection(Protocol):
"""
Protocol class/interface for the connection object.
Exposes:
- get
- put
- post
- delete
"""

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,
content_type: str = "application/json",
) -> 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
jsondata: Valid JSON as string
content_type: HTTP Content-Type [default: 'application/json']
"""

def post(
self,
route: str,
jsondata: str | None = None,
content_type: str = "application/json",
) -> dict[str, Any]:
"""
Make a HTTP POST request to the server to which this connection has been established.
Args:
route: route that will be called on the server
jsondata: Valid JSON as string
content_type: HTTP Content-Type [default: 'application/json']
"""

def delete(
self,
route: str,
params: dict[str, Any] | 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
"""

def get_token(self) -> str:
"""
Return the token of this connection.
Raises:
BaseError: if no token is available
"""

def login(self, email: str, password: str) -> None:
"""
Retrieve a session token and store it as class attribute.
Args:
email: email address of the user
password: password of the user
Raises:
BaseError: if DSP-API returns no token with the provided user credentials
"""

def logout(self) -> None:
"""
Delete the token on the server and in this class.
"""
Expand Up @@ -14,7 +14,7 @@ def check_for_api_error(response: requests.Response) -> None:
Check the response of an API request if it contains an error raised by DSP-API.
Args:
res: The requests.Response object that is returned by the API request
response: The requests.Response object that is returned by the API request
Raises:
BaseError: If the status code of the response is not 200
Expand All @@ -30,7 +30,7 @@ def check_for_api_error(response: requests.Response) -> None:


@dataclass
class Connection:
class ConnectionLive:
"""
A Connection instance represents a connection to a DSP server.
Expand Down Expand Up @@ -94,6 +94,9 @@ def get_token(self) -> str:
Returns:
token
Raises:
BaseError: if no token is available
"""
if not self.token:
raise BaseError("No token available.")
Expand All @@ -116,6 +119,7 @@ def _write_request_to_file(
url: complete URL (server + route of DSP-API) that was called
headers: headers of the HTTP request
jsondata: data sent to the server
params: additional parameters for the HTTP request
response: response of the server
"""
if response.status_code == 200:
Expand Down Expand Up @@ -240,6 +244,9 @@ def put(
route: route that will be called on the server
jsondata: Valid JSON as string
content_type: HTTP Content-Type [default: 'application/json']
Returns:
response from server
"""
# timeout must be None,
# otherwise the client can get a timeout error while the API is still processing the request
Expand Down
2 changes: 1 addition & 1 deletion src/dsp_tools/fast_xmlupload/upload_files.py
Expand Up @@ -9,7 +9,7 @@
import requests
from regex import regex

from dsp_tools.models.connection import Connection
from dsp_tools.connection.connection import Connection
from dsp_tools.models.exceptions import UserError
from dsp_tools.utils.create_logger import get_logger
from dsp_tools.utils.shared import login
Expand Down
16 changes: 6 additions & 10 deletions src/dsp_tools/models/group.py
Expand Up @@ -29,7 +29,7 @@
from typing import Any, Optional, Union
from urllib.parse import quote_plus

from dsp_tools.models.connection import Connection
from dsp_tools.connection.connection import Connection
from dsp_tools.models.exceptions import BaseError
from dsp_tools.models.helpers import Actions
from dsp_tools.models.langstring import LangString
Expand Down Expand Up @@ -75,7 +75,7 @@ class Group(Model): # pylint: disable=too-many-instance-attributes
ROUTE_SLASH: str = ROUTE + "/"

_iri: Optional[str]
_name: str
_name: str | None
_descriptions: LangString
_project: str
_selfjoin: bool
Expand Down Expand Up @@ -115,7 +115,7 @@ def name(self) -> Optional[str]:
return self._name

@name.setter
def name(self, value: str):
def name(self, value: str) -> None:
self._name = value
self._changed.add("name")

Expand All @@ -129,13 +129,9 @@ def descriptions(self, value: Optional[LangString]) -> None:
self._changed.add("descriptions")

@property
def project(self):
def project(self) -> str:
return self._project

@project.setter
def project(self, value: str):
raise BaseError("project id cannot be modified!")

@property
def selfjoin(self) -> bool:
return self._selfjoin
Expand All @@ -155,7 +151,7 @@ def status(self, value: bool) -> None:
self._changed.add("status")

@classmethod
def fromJsonObj(cls, con: Connection, json_obj: Any):
def fromJsonObj(cls, con: Connection, json_obj: Any) -> Group:
group_id = json_obj.get("id")
if group_id is None:
raise BaseError('Group "id" is missing')
Expand Down Expand Up @@ -185,7 +181,7 @@ def fromJsonObj(cls, con: Connection, json_obj: Any):
status=status,
)

def toJsonObj(self, action: Actions):
def toJsonObj(self, action: Actions) -> dict[str, Any]:
tmp = {}
if action == Actions.Create:
if self._name is None:
Expand Down
8 changes: 5 additions & 3 deletions src/dsp_tools/models/helpers.py
@@ -1,5 +1,7 @@
# pylint: disable=missing-class-docstring,missing-function-docstring

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum, unique
from typing import Any, Optional, Pattern, Union
Expand Down Expand Up @@ -436,15 +438,15 @@ def __ge__(self, other: "DateTimeStamp") -> bool:
other = DateTimeStamp(other)
return self._dateTimeStamp >= other._dateTimeStamp

def __ne__(self, other: "DateTimeStamp") -> bool:
def __ne__(self, other: DateTimeStamp) -> bool:
if isinstance(other, str):
other = DateTimeStamp(other)
return self._dateTimeStamp != other._dateTimeStamp

def __str__(self: "DateTimeStamp") -> Union[None, str]:
def __str__(self: "DateTimeStamp") -> str:
return self._dateTimeStamp

def toJsonObj(self):
def toJsonObj(self) -> dict[str, str]:
return {"@type": "xsd:dateTimeStamp", "@value": self._dateTimeStamp}


Expand Down
23 changes: 8 additions & 15 deletions src/dsp_tools/models/listnode.py
Expand Up @@ -21,13 +21,14 @@
DELETE
* NOT YET IMPLEMENTED!
"""
from __future__ import annotations

import json
from pprint import pprint
from typing import Any, Optional, Union
from urllib.parse import quote_plus

from dsp_tools.models.connection import Connection
from dsp_tools.connection.connection import Connection
from dsp_tools.models.exceptions import BaseError
from dsp_tools.models.helpers import Actions
from dsp_tools.models.langstring import LangString, Languages
Expand Down Expand Up @@ -208,10 +209,6 @@ def __init__(
def iri(self) -> Optional[str]:
return self._id

@iri.setter
def iri(self, value: str) -> None:
raise BaseError("ListNode iri cannot be modified!")

@property
def project(self) -> Optional[str]:
return self._project
Expand Down Expand Up @@ -299,7 +296,7 @@ def parent(self) -> Optional[str]:
return self._parent if self._parent else None

@parent.setter
def parent(self, value: Union[str, "ListNode"]):
def parent(self, value: Union[str, "ListNode"]) -> None:
if isinstance(value, ListNode):
self._parent = value.iri
else:
Expand Down Expand Up @@ -354,12 +351,8 @@ def __getChildren(
def rootNodeIri(self) -> Optional[str]:
return self._rootNodeIri

@rootNodeIri.setter
def rootNodeIri(self, value: str):
raise BaseError("rootNodeIri cannot be set!")

@classmethod
def fromJsonObj(cls, con: Connection, json_obj: Any) -> Any:
def fromJsonObj(cls, con: Connection, json_obj: Any) -> ListNode:
"""
Internal method! Should not be used directly!
Expand Down Expand Up @@ -402,7 +395,7 @@ def fromJsonObj(cls, con: Connection, json_obj: Any) -> Any:
rootNodeIri=rootNodeIri,
)

def toJsonObj(self, action: Actions, listIri: str = None) -> Any:
def toJsonObj(self, action: Actions, listIri: str | None = None) -> dict[str, Any]:
"""
Internal method! Should not be used directly!
Expand Down Expand Up @@ -446,7 +439,7 @@ def toJsonObj(self, action: Actions, listIri: str = None) -> Any:

return tmp

def create(self) -> "ListNode":
def create(self) -> ListNode:
"""
Create a new List
Expand Down Expand Up @@ -522,7 +515,7 @@ def getAllNodes(self) -> "ListNode":
return root

@staticmethod
def getAllLists(con: Connection, project_iri: Optional[str] = None) -> list["ListNode"]:
def getAllLists(con: Connection, project_iri: Optional[str] = None) -> list[ListNode]:
"""
Get all lists. If a project IRI is given, it returns the lists of the specified project
Expand All @@ -538,7 +531,7 @@ def getAllLists(con: Connection, project_iri: Optional[str] = None) -> list["Lis
raise BaseError("Request got no lists!")
return list(map(lambda a: ListNode.fromJsonObj(con, a), result["lists"]))

def _createDefinitionFileObj(self, children: list["ListNode"]):
def _createDefinitionFileObj(self, children: list[ListNode]) -> list[dict[str, Any]]:
"""
Create an object that corresponds to the syntax of the input to "create_onto".
Node: This method must be used only internally (for recursion)!!
Expand Down
5 changes: 1 addition & 4 deletions src/dsp_tools/models/model.py
@@ -1,5 +1,4 @@
from dsp_tools.models.connection import Connection
from dsp_tools.models.exceptions import BaseError
from dsp_tools.connection.connection import Connection


class Model: # pylint: disable=too-few-public-methods
Expand All @@ -9,7 +8,5 @@ class Model: # pylint: disable=too-few-public-methods
_changed: set[str]

def __init__(self, con: Connection):
if not isinstance(con, Connection):
raise BaseError('"con"-parameter must be an instance of Connection')
self._con = con
self._changed = set()

0 comments on commit 87c1e18

Please sign in to comment.