rcer-iot-client-pkg 0.5.0__py3-none-any.whl → 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rcer_iot_client_pkg/__init__.py +2 -1
- rcer_iot_client_pkg/general_types/api/__init__.py +3 -0
- rcer_iot_client_pkg/general_types/error_types/api/update_thies_data_error_types.py +14 -1
- rcer_iot_client_pkg/libs/sharepoint_client/clients/sharepoint_rest_api.py +15 -10
- rcer_iot_client_pkg/services/epii/controllers/update_thies_data.py +3 -3
- {rcer_iot_client_pkg-0.5.0.dist-info → rcer_iot_client_pkg-0.5.2.dist-info}/METADATA +14 -8
- {rcer_iot_client_pkg-0.5.0.dist-info → rcer_iot_client_pkg-0.5.2.dist-info}/RECORD +9 -16
- rcer_iot_client_pkg/libs/async_http_client/__init__.py +0 -10
- rcer_iot_client_pkg/libs/async_http_client/async_http_client.py +0 -34
- rcer_iot_client_pkg/libs/async_http_client/async_http_client_contract.py +0 -29
- rcer_iot_client_pkg/libs/async_http_client/clients/__init__.py +0 -0
- rcer_iot_client_pkg/libs/async_http_client/clients/aiohttp_client.py +0 -50
- rcer_iot_client_pkg/libs/async_http_client/types/__init__.py +0 -3
- rcer_iot_client_pkg/libs/async_http_client/types/async_http_client_types.py +0 -17
- {rcer_iot_client_pkg-0.5.0.dist-info → rcer_iot_client_pkg-0.5.2.dist-info}/LICENSE +0 -0
- {rcer_iot_client_pkg-0.5.0.dist-info → rcer_iot_client_pkg-0.5.2.dist-info}/WHEEL +0 -0
rcer_iot_client_pkg/__init__.py
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
import json
|
2
|
+
|
3
|
+
|
1
4
|
class ThiesConnectionError(Exception):
|
2
5
|
"""Raised when unable to connect to the THIES FTP Server"""
|
3
6
|
|
@@ -26,5 +29,15 @@ class ThiesFetchingError(Exception):
|
|
26
29
|
class SharePointFetchingError(Exception):
|
27
30
|
"""Raised when there is an error fetching file names from the RCER cloud."""
|
28
31
|
|
32
|
+
def __init__(self, *args, reason):
|
33
|
+
super().__init__(*args, reason)
|
34
|
+
self.reason = reason
|
35
|
+
|
29
36
|
def __str__(self):
|
30
|
-
|
37
|
+
try:
|
38
|
+
_, internal_metadata = self.reason.__str__().split(",", 1)
|
39
|
+
internal_metadata_dict = json.loads(internal_metadata)
|
40
|
+
return internal_metadata_dict["error_description"]
|
41
|
+
|
42
|
+
except json.decoder.JSONDecodeError:
|
43
|
+
return self.reason.__str__()
|
@@ -65,17 +65,22 @@ class SharepointRestAPI(SharepointClientContract):
|
|
65
65
|
}
|
66
66
|
|
67
67
|
async def __aenter__(self) -> "SharepointRestAPI":
|
68
|
-
|
69
|
-
|
68
|
+
try:
|
69
|
+
self.credentials = await self._load_credentials()
|
70
|
+
site_url = f"https://{self.tenant_name}.sharepoint.com"
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
72
|
+
self.base_headers = {
|
73
|
+
"Authorization": f"Bearer {self.credentials['access_token']}",
|
74
|
+
"Accept": "application/json",
|
75
|
+
"Content-Type": "application/json",
|
76
|
+
}
|
77
|
+
self.base_url = f"{site_url}/sites/{self.site_name}/_api/"
|
78
|
+
self.session = ClientSession(
|
79
|
+
headers=self.base_headers, base_url=self.base_url
|
80
|
+
)
|
81
|
+
return self
|
82
|
+
except ClientError as error:
|
83
|
+
raise ConnectionError(error)
|
79
84
|
|
80
85
|
async def __aexit__(
|
81
86
|
self, _exc_type: type[BaseException], _exc_val: BaseException, _exc_tb: Any
|
@@ -73,14 +73,14 @@ class UpdateThiesDataController:
|
|
73
73
|
except SharepointClientError as error:
|
74
74
|
return UpdateThiesDataControllerOutput(
|
75
75
|
message="Sharepoint Client initialization fails.",
|
76
|
-
status=HTTPStatus.
|
76
|
+
status=HTTPStatus.INTERNAL_SERVER_ERROR.value,
|
77
77
|
metadata={"error": error.__str__()},
|
78
78
|
)
|
79
79
|
|
80
80
|
except SharePointFetchingError as error:
|
81
81
|
return UpdateThiesDataControllerOutput(
|
82
|
-
message="An error occurred while retrieving file names from
|
83
|
-
status=HTTPStatus.
|
82
|
+
message="An error occurred while retrieving file names from Microsoft SharePoint",
|
83
|
+
status=HTTPStatus.BAD_REQUEST.value,
|
84
84
|
metadata={"error": error.__str__()},
|
85
85
|
)
|
86
86
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: rcer_iot_client_pkg
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.2
|
4
4
|
Summary: A client library for IoT projects in the RCER initiative
|
5
5
|
License: MIT
|
6
6
|
Author: pedropablozavalat
|
@@ -45,23 +45,29 @@ api_client = EpiiAPI()
|
|
45
45
|
The library provides a method to synchronize THIES Data Logger files with the RCER SharePoint client. This method updates the folder containing binary files with meteorological data:
|
46
46
|
|
47
47
|
```python
|
48
|
+
from rcer_iot_client_pkg import EpiiAPIConfig
|
48
49
|
import asyncio
|
49
50
|
|
50
51
|
async def update_thies_data():
|
51
|
-
|
52
|
-
ftp_port=
|
53
|
-
ftp_host=
|
54
|
-
|
55
|
-
|
52
|
+
config = EpiiAPIConfig(
|
53
|
+
ftp_port=FTP_PORT,
|
54
|
+
ftp_host=FTP_HOST,
|
55
|
+
ftp_user=FTP_USER,
|
56
|
+
ftp_password=FTP_PASSWORD,
|
57
|
+
sharepoint_client_id=SHAREPOINT_CLIENT_ID,
|
58
|
+
sharepoint_client_secret=SHAREPOINT_CLIENT_SECRET,
|
59
|
+
sharepoint_tenant_id=SHAREPOINT_TENANT_ID,
|
60
|
+
sharepoint_tenant_name=SHAREPOINT_TENANT_NAME,
|
61
|
+
sharepoint_site_name=SHAREPOINT_SITE_NAME
|
56
62
|
)
|
63
|
+
response = await api_client.update_thies_data(config)
|
57
64
|
return response
|
58
65
|
|
59
66
|
asyncio.run(update_thies_data())
|
60
67
|
```
|
61
68
|
|
62
69
|
**Notes:**
|
63
|
-
- Store sensitive data like `
|
64
|
-
- Ensure `asyncio` is installed to run concurrent code with `EpiiAPI` methods.
|
70
|
+
- Store sensitive data like `FTP_PASSWORD`, `FTP_USER`, and SharePoint credentials securely. Use environment variables or a secrets management tool to avoid hardcoding sensitive information in your codebase.
|
65
71
|
|
66
72
|
## Development
|
67
73
|
|
@@ -1,19 +1,12 @@
|
|
1
|
-
rcer_iot_client_pkg/__init__.py,sha256=
|
1
|
+
rcer_iot_client_pkg/__init__.py,sha256=ynL8ADM5u2SQTdGfqVLDWEu9bYQnp6znMCEXnhu5zRA,272
|
2
2
|
rcer_iot_client_pkg/general_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
rcer_iot_client_pkg/general_types/api/__init__.py,sha256=
|
3
|
+
rcer_iot_client_pkg/general_types/api/__init__.py,sha256=1afRFS8AkxkxOWmcNLFI3H1lb-w_5dVJMMDKobBYlOY,80
|
4
4
|
rcer_iot_client_pkg/general_types/api/update_thies_data_types.py,sha256=NqViiGKm1FdhgxJrNkLBLzqI-8SnPieAYv21kEN752U,963
|
5
5
|
rcer_iot_client_pkg/general_types/error_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
rcer_iot_client_pkg/general_types/error_types/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
rcer_iot_client_pkg/general_types/error_types/api/update_thies_data_error_types.py,sha256=
|
7
|
+
rcer_iot_client_pkg/general_types/error_types/api/update_thies_data_error_types.py,sha256=Wl9zd7qM2KeZF-h28dqSd6L9N8azOEagD837zoBgpAc,1269
|
8
8
|
rcer_iot_client_pkg/general_types/error_types/common/__init__.py,sha256=yOBLZbt64Ki9Q0IJ0tMAubgq7PtrQ7XQ3RgtAzyOjiE,170
|
9
9
|
rcer_iot_client_pkg/general_types/error_types/common/common_types.py,sha256=n5yuw-gVtkrtNfmaZ83ZkYxYHGl4jynOLUB9C8Tr32w,474
|
10
|
-
rcer_iot_client_pkg/libs/async_http_client/__init__.py,sha256=LF1KEh5Dhu0j9qZhSBGOsD-PWUFKEvS2oE0eUCyNLd0,279
|
11
|
-
rcer_iot_client_pkg/libs/async_http_client/async_http_client.py,sha256=wlOvjThS8wnpyqdcY7HbeEx1X3eVNKxJ8DuCJtx0wFk,1135
|
12
|
-
rcer_iot_client_pkg/libs/async_http_client/async_http_client_contract.py,sha256=PCoTt4R6vFwjvyOTETQ8jEw1uqzAQC3PYiIlg_d7iRI,816
|
13
|
-
rcer_iot_client_pkg/libs/async_http_client/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
-
rcer_iot_client_pkg/libs/async_http_client/clients/aiohttp_client.py,sha256=exJ7yq_9ukKoGhdyEwUEe8N5syvEsv2vsQ5eJcQbKSc,1837
|
15
|
-
rcer_iot_client_pkg/libs/async_http_client/types/__init__.py,sha256=TnJOBj5R6rVA5XAiId5dpQM6KjMIfi8KJkCCav7n930,154
|
16
|
-
rcer_iot_client_pkg/libs/async_http_client/types/async_http_client_types.py,sha256=d-_rHQ21ajTr5AI66fH3-cpJzF8VhSqG0BHbVrlwND8,329
|
17
10
|
rcer_iot_client_pkg/libs/ftp_client/__init__.py,sha256=dW2Yutgc7mJJJzgKLhWKXMgQ6KIWJYfFa1sGpjHH5xU,191
|
18
11
|
rcer_iot_client_pkg/libs/ftp_client/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
12
|
rcer_iot_client_pkg/libs/ftp_client/clients/aioftp_client.py,sha256=5CI9J4axAeoqlN-cYToVcPF9sAZEk0vIOhPOHrhlpk4,1633
|
@@ -22,7 +15,7 @@ rcer_iot_client_pkg/libs/ftp_client/ftp_client_contract.py,sha256=2x1MPZNFVw3l-s
|
|
22
15
|
rcer_iot_client_pkg/libs/ftp_client/types/__init__.py,sha256=syfwf9feP4QK7fkCTfl4j8l11ic-jHtfi1DE2chaWbs,155
|
23
16
|
rcer_iot_client_pkg/libs/ftp_client/types/ftp_client_types.py,sha256=e4SmYkewldulaD8ms2q75zVgLFXyBxBqoa_L-IQOmso,256
|
24
17
|
rcer_iot_client_pkg/libs/sharepoint_client/__init__.py,sha256=v7h-cNsK-BaPp-hTU3NWRNXYRD9ztU-hsCk0eNRPIKA,334
|
25
|
-
rcer_iot_client_pkg/libs/sharepoint_client/clients/sharepoint_rest_api.py,sha256=
|
18
|
+
rcer_iot_client_pkg/libs/sharepoint_client/clients/sharepoint_rest_api.py,sha256=Jxa8ZODVHVMdIwTCcE1tNS7z2JJDPMbgSshLvQ7H0Uk,5430
|
26
19
|
rcer_iot_client_pkg/libs/sharepoint_client/sharepoint_client.py,sha256=UYfAhckzmal5-Jjvbz0P4gt64w5gOYJYksVSHXdJPiQ,1234
|
27
20
|
rcer_iot_client_pkg/libs/sharepoint_client/sharepoint_client_contract.py,sha256=xqNHzCjp7GvUGGUox9YTJj2QJgTc5819t2etOk8X26o,485
|
28
21
|
rcer_iot_client_pkg/libs/sharepoint_client/types/sharepoint_client_types.py,sha256=gfXhvKuqZjxSdbgbEgGF1EKYDDq7CjK9Tdth-Waq5xo,414
|
@@ -33,13 +26,13 @@ rcer_iot_client_pkg/services/epii/constants/update_thies_data_constants.py,sha25
|
|
33
26
|
rcer_iot_client_pkg/services/epii/controllers/__init__.py,sha256=mCdGgKGDgGxCtRoiZN9Rki-fTOyOuJWw9e7festpQYA,98
|
34
27
|
rcer_iot_client_pkg/services/epii/controllers/types/__init__.py,sha256=xzky-oTSojLNkWETp_k8a4dcXYvYSQY0VhWo23Yhb8U,195
|
35
28
|
rcer_iot_client_pkg/services/epii/controllers/types/update_thies_data_types.py,sha256=M5rYsjgqTndhPIRi77McxLyTrjmGsoVUIAGPETNXz9U,374
|
36
|
-
rcer_iot_client_pkg/services/epii/controllers/update_thies_data.py,sha256=
|
29
|
+
rcer_iot_client_pkg/services/epii/controllers/update_thies_data.py,sha256=YWoXXNVaddCt8D_rTb8cU7gYR6l1VNwrjtldbqLIQ3A,4060
|
37
30
|
rcer_iot_client_pkg/services/epii/use_cases/types/__init__.py,sha256=u6fyodOEJE2j6FMqJux40Xf9ccYAi-UUYxqT-Kzc0kE,199
|
38
31
|
rcer_iot_client_pkg/services/epii/use_cases/types/update_thies_data_types.py,sha256=C0TU50KKYodpaX87OnG0MnHyGY4gRzmluUHk-esCEVU,635
|
39
32
|
rcer_iot_client_pkg/services/epii/use_cases/update_thies_data.py,sha256=Tc12NUnZ0or2Tz5BA9jE62I5XxmKvZ__v4w5dTBk_5A,5052
|
40
33
|
rcer_iot_client_pkg/services/epii/utils/__init__.py,sha256=cYt2tvq65_OMjFaqb8-CCC7IGCQgFd4ziEUWJV7s1iY,98
|
41
34
|
rcer_iot_client_pkg/services/epii/utils/update_thies_data_utils.py,sha256=-q8t-xZmpwFDADGlDu0S7EKvZK7R0GUR6JqOxQShWgE,415
|
42
|
-
rcer_iot_client_pkg-0.5.
|
43
|
-
rcer_iot_client_pkg-0.5.
|
44
|
-
rcer_iot_client_pkg-0.5.
|
45
|
-
rcer_iot_client_pkg-0.5.
|
35
|
+
rcer_iot_client_pkg-0.5.2.dist-info/LICENSE,sha256=NWpf6b38xgBWPBo5HZsCbdfp9hZSliEbRqWQgm0fkOo,1076
|
36
|
+
rcer_iot_client_pkg-0.5.2.dist-info/METADATA,sha256=BWgFty5jiYYErS5W7KgoFumdMKUy6ZfciJ3r309PkzA,3860
|
37
|
+
rcer_iot_client_pkg-0.5.2.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
38
|
+
rcer_iot_client_pkg-0.5.2.dist-info/RECORD,,
|
@@ -1,10 +0,0 @@
|
|
1
|
-
"""Export defined type classes."""
|
2
|
-
|
3
|
-
from .async_http_client import AsyncHTTPClient
|
4
|
-
from .types.async_http_client_types import (
|
5
|
-
AsyncHttpClientInitArgs,
|
6
|
-
GetArgs,
|
7
|
-
UploadFileArgs,
|
8
|
-
)
|
9
|
-
|
10
|
-
__all__ = ["AsyncHTTPClient", "AsyncHttpClientInitArgs", "GetArgs", "UploadFileArgs"]
|
@@ -1,34 +0,0 @@
|
|
1
|
-
from typing import Any
|
2
|
-
|
3
|
-
from .async_http_client_contract import AsyncHTTPClientContract
|
4
|
-
from .clients.aiohttp_client import AioHttpClient
|
5
|
-
from .types.async_http_client_types import (
|
6
|
-
AsyncHttpClientInitArgs,
|
7
|
-
GetArgs,
|
8
|
-
UploadFileArgs,
|
9
|
-
)
|
10
|
-
|
11
|
-
|
12
|
-
class AsyncHTTPClient(AsyncHTTPClientContract):
|
13
|
-
CLIENTS = {"aiohttp_client"}
|
14
|
-
|
15
|
-
def __init__(self, args: AsyncHttpClientInitArgs) -> None:
|
16
|
-
if args.client_name not in AsyncHTTPClient.CLIENTS:
|
17
|
-
msg = f"Unsupported client '{args.client_name}'"
|
18
|
-
raise KeyError(msg)
|
19
|
-
self.client_name = args.client_name
|
20
|
-
|
21
|
-
if args.client_name == "aiohttp_client":
|
22
|
-
self.client_obj = AioHttpClient(args)
|
23
|
-
|
24
|
-
async def __aenter__(self):
|
25
|
-
return await self.client_obj.__aenter__()
|
26
|
-
|
27
|
-
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
28
|
-
await self.client_obj.__aexit__(exc_type, exc_val, exc_tb)
|
29
|
-
|
30
|
-
async def get(self, args: GetArgs) -> dict[str, Any]:
|
31
|
-
return await self.client_obj.get(args)
|
32
|
-
|
33
|
-
async def upload_file(self, args: UploadFileArgs) -> dict[str, Any]:
|
34
|
-
return await self.client_obj.upload_file(args)
|
@@ -1,29 +0,0 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from .types.async_http_client_types import GetArgs, UploadFileArgs
|
5
|
-
|
6
|
-
|
7
|
-
class AsyncHTTPClientContract(ABC):
|
8
|
-
"""
|
9
|
-
A contract for asynchronous HTTP client implementations.
|
10
|
-
|
11
|
-
This abstract base class defines the required methods for performing
|
12
|
-
HTTP GET requests and uploading files asynchronously.
|
13
|
-
|
14
|
-
Methods.
|
15
|
-
-------
|
16
|
-
get(args: GetArgs) -> dict[str, Any]
|
17
|
-
Perform an HTTP GET request with the specified arguments.
|
18
|
-
|
19
|
-
upload_file(args: UploadFileArgs) -> dict[str, Any]
|
20
|
-
Upload a file using the specified arguments.
|
21
|
-
"""
|
22
|
-
|
23
|
-
@abstractmethod
|
24
|
-
async def get(self, args: GetArgs) -> dict[str, Any]:
|
25
|
-
pass
|
26
|
-
|
27
|
-
@abstractmethod
|
28
|
-
async def upload_file(self, args: UploadFileArgs) -> dict[str, Any]:
|
29
|
-
pass
|
File without changes
|
@@ -1,50 +0,0 @@
|
|
1
|
-
from typing import Any
|
2
|
-
|
3
|
-
from aiohttp import ClientError, ClientSession
|
4
|
-
|
5
|
-
from rcer_iot_client_pkg.libs.async_http_client.async_http_client_contract import (
|
6
|
-
AsyncHTTPClientContract,
|
7
|
-
)
|
8
|
-
from rcer_iot_client_pkg.libs.async_http_client.types.async_http_client_types import (
|
9
|
-
AsyncHttpClientInitArgs,
|
10
|
-
GetArgs,
|
11
|
-
UploadFileArgs,
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
class AioHttpClient(AsyncHTTPClientContract):
|
16
|
-
def __init__(self, args: AsyncHttpClientInitArgs) -> None:
|
17
|
-
self.access_token = args.access_token
|
18
|
-
self.base_url = args.base_url
|
19
|
-
self.headers = self._build_headers()
|
20
|
-
self.session: ClientSession | None = None
|
21
|
-
|
22
|
-
def _build_headers(self) -> dict:
|
23
|
-
return {"Authorization": f"Bearer {self.access_token}"}
|
24
|
-
|
25
|
-
async def __aenter__(self) -> "AioHttpClient":
|
26
|
-
self.session = ClientSession(headers=self.headers, base_url=self.base_url)
|
27
|
-
return self
|
28
|
-
|
29
|
-
async def __aexit__(
|
30
|
-
self, _exc_type: type[BaseException], _exc_val: BaseException, _exc_tb: Any
|
31
|
-
) -> None:
|
32
|
-
await self.session.close()
|
33
|
-
|
34
|
-
async def get(self, args: GetArgs) -> dict[str, Any]:
|
35
|
-
try:
|
36
|
-
endpoint, params = args.endpoint.lstrip("/"), args.params
|
37
|
-
response = await self.session.get(endpoint, params=params)
|
38
|
-
response.raise_for_status()
|
39
|
-
return await response.json()
|
40
|
-
except ClientError as error:
|
41
|
-
raise ConnectionError(error) from error
|
42
|
-
|
43
|
-
async def upload_file(self, args: UploadFileArgs) -> dict[str, Any]:
|
44
|
-
try:
|
45
|
-
endpoint, file_bytes = args.endpoint.lstrip("/"), args.file_bytes
|
46
|
-
response = await self.session.put(endpoint, data=file_bytes)
|
47
|
-
response.raise_for_status()
|
48
|
-
return await response.json()
|
49
|
-
except ClientError as error:
|
50
|
-
raise ConnectionError(error) from error
|
@@ -1,17 +0,0 @@
|
|
1
|
-
from pydantic import BaseModel, Field
|
2
|
-
|
3
|
-
|
4
|
-
class AsyncHttpClientInitArgs(BaseModel):
|
5
|
-
access_token: str
|
6
|
-
base_url: str
|
7
|
-
client_name: str = "aiohttp_client"
|
8
|
-
|
9
|
-
|
10
|
-
class GetArgs(BaseModel):
|
11
|
-
endpoint: str
|
12
|
-
params: dict | None = Field(default=None)
|
13
|
-
|
14
|
-
|
15
|
-
class UploadFileArgs(BaseModel):
|
16
|
-
endpoint: str
|
17
|
-
file_bytes: bytes
|
File without changes
|
File without changes
|