diaspora-event-sdk 0.0.13__py3-none-any.whl → 0.0.16__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.
- diaspora_event_sdk/__init__.py +3 -8
- diaspora_event_sdk/sdk/client.py +27 -18
- diaspora_event_sdk/sdk/kafka_client.py +16 -4
- diaspora_event_sdk/sdk/web_client.py +4 -4
- diaspora_event_sdk/version.py +1 -1
- {diaspora_event_sdk-0.0.13.dist-info → diaspora_event_sdk-0.0.16.dist-info}/METADATA +19 -1
- {diaspora_event_sdk-0.0.13.dist-info → diaspora_event_sdk-0.0.16.dist-info}/RECORD +11 -10
- tests/unit/test_client.py +80 -0
- {diaspora_event_sdk-0.0.13.dist-info → diaspora_event_sdk-0.0.16.dist-info}/LICENSE +0 -0
- {diaspora_event_sdk-0.0.13.dist-info → diaspora_event_sdk-0.0.16.dist-info}/WHEEL +0 -0
- {diaspora_event_sdk-0.0.13.dist-info → diaspora_event_sdk-0.0.16.dist-info}/top_level.txt +0 -0
diaspora_event_sdk/__init__.py
CHANGED
|
@@ -6,11 +6,6 @@ from diaspora_event_sdk.version import __version__ as _version
|
|
|
6
6
|
__author__ = "The Diaspora Event Team"
|
|
7
7
|
__version__ = _version
|
|
8
8
|
|
|
9
|
-
from diaspora_event_sdk.sdk.client import Client
|
|
10
|
-
from diaspora_event_sdk.sdk.kafka_client import
|
|
11
|
-
|
|
12
|
-
if kafka_available:
|
|
13
|
-
from diaspora_event_sdk.sdk.kafka_client import KafkaProducer, KafkaConsumer
|
|
14
|
-
__all__ = ("Client", "KafkaProducer", "KafkaConsumer")
|
|
15
|
-
else:
|
|
16
|
-
__all__ = ("Client")
|
|
9
|
+
from diaspora_event_sdk.sdk.client import Client
|
|
10
|
+
from diaspora_event_sdk.sdk.kafka_client import KafkaProducer, KafkaConsumer
|
|
11
|
+
__all__ = ("Client", "KafkaProducer", "KafkaConsumer")
|
diaspora_event_sdk/sdk/client.py
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
1
|
import json
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
|
-
from diaspora_event_sdk.sdk.login_manager import
|
|
5
|
-
|
|
4
|
+
from diaspora_event_sdk.sdk.login_manager import (
|
|
5
|
+
LoginManager,
|
|
6
|
+
LoginManagerProtocol,
|
|
7
|
+
requires_login,
|
|
8
|
+
)
|
|
6
9
|
|
|
10
|
+
from ._environments import DIASPORA_RESOURCE_SERVER, TOKEN_EXCHANGE
|
|
7
11
|
|
|
8
|
-
class Client:
|
|
9
12
|
|
|
13
|
+
class Client:
|
|
10
14
|
def __init__(
|
|
11
15
|
self,
|
|
12
16
|
environment: Optional[str] = None,
|
|
@@ -21,9 +25,7 @@ class Client:
|
|
|
21
25
|
self.login_manager = LoginManager(environment=environment)
|
|
22
26
|
self.login_manager.ensure_logged_in()
|
|
23
27
|
|
|
24
|
-
self.web_client = self.login_manager.get_web_client(
|
|
25
|
-
base_url=TOKEN_EXCHANGE
|
|
26
|
-
)
|
|
28
|
+
self.web_client = self.login_manager.get_web_client(base_url=TOKEN_EXCHANGE)
|
|
27
29
|
self.auth_client = self.login_manager.get_auth_client()
|
|
28
30
|
self.subject_openid = self.auth_client.oauth2_userinfo()["sub"]
|
|
29
31
|
|
|
@@ -41,20 +43,26 @@ class Client:
|
|
|
41
43
|
raise Exception("should not happen")
|
|
42
44
|
|
|
43
45
|
tokens = self.login_manager._token_storage.get_token_data(
|
|
44
|
-
DIASPORA_RESOURCE_SERVER
|
|
45
|
-
|
|
46
|
+
DIASPORA_RESOURCE_SERVER
|
|
47
|
+
)
|
|
48
|
+
tokens["access_key"], tokens["secret_key"] = (
|
|
49
|
+
resp["access_key"],
|
|
50
|
+
resp["secret_key"],
|
|
51
|
+
)
|
|
46
52
|
with self.login_manager._access_lock:
|
|
47
53
|
self.login_manager._token_storage._connection.executemany(
|
|
48
54
|
"REPLACE INTO token_storage(namespace, resource_server, token_data_json) "
|
|
49
55
|
"VALUES(?, ?, ?)",
|
|
50
56
|
[
|
|
51
|
-
(
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
(
|
|
58
|
+
self.login_manager._token_storage.namespace,
|
|
59
|
+
DIASPORA_RESOURCE_SERVER,
|
|
60
|
+
json.dumps(tokens),
|
|
61
|
+
)
|
|
54
62
|
],
|
|
55
63
|
)
|
|
56
64
|
self.login_manager._token_storage._connection.commit()
|
|
57
|
-
return {"username": self.subject_openid, "password": tokens[
|
|
65
|
+
return {"username": self.subject_openid, "password": tokens["secret_key"]}
|
|
58
66
|
|
|
59
67
|
@requires_login
|
|
60
68
|
def retrieve_key(self):
|
|
@@ -62,29 +70,30 @@ class Client:
|
|
|
62
70
|
Attempt to retrieve the key from local token storage, and call create_key if local key is not found
|
|
63
71
|
"""
|
|
64
72
|
tokens = self.login_manager._token_storage.get_token_data(
|
|
65
|
-
DIASPORA_RESOURCE_SERVER
|
|
73
|
+
DIASPORA_RESOURCE_SERVER
|
|
74
|
+
)
|
|
66
75
|
if tokens is None or "access_key" not in tokens or "secret_key" not in tokens:
|
|
67
76
|
return self.create_key()
|
|
68
77
|
else:
|
|
69
|
-
return {"username": self.subject_openid, "password": tokens[
|
|
78
|
+
return {"username": self.subject_openid, "password": tokens["secret_key"]}
|
|
70
79
|
|
|
71
80
|
@requires_login
|
|
72
81
|
def list_topics(self):
|
|
73
82
|
"""
|
|
74
|
-
Retrieves the list of topics associated with the user's OpenID.
|
|
83
|
+
Retrieves the list of topics associated with the user's OpenID.
|
|
75
84
|
"""
|
|
76
85
|
return self.web_client.list_topics(self.subject_openid)
|
|
77
86
|
|
|
78
87
|
@requires_login
|
|
79
88
|
def register_topic(self, topic):
|
|
80
89
|
"""
|
|
81
|
-
Registers a new topic under the user's OpenID.
|
|
90
|
+
Registers a new topic under the user's OpenID.
|
|
82
91
|
"""
|
|
83
92
|
return self.web_client.register_topic(self.subject_openid, topic)
|
|
84
93
|
|
|
85
94
|
@requires_login
|
|
86
95
|
def unregister_topic(self, topic):
|
|
87
96
|
"""
|
|
88
|
-
Unregisters a topic from the user's OpenID.
|
|
97
|
+
Unregisters a topic from the user's OpenID.
|
|
89
98
|
"""
|
|
90
99
|
return self.web_client.unregister_topic(self.subject_openid, topic)
|
|
@@ -8,7 +8,9 @@ from .client import Client
|
|
|
8
8
|
# If kafka-python is not installed, Kafka functionality is not available through diaspora-event-sdk.
|
|
9
9
|
kafka_available = True
|
|
10
10
|
try:
|
|
11
|
-
from kafka import KafkaProducer
|
|
11
|
+
from kafka import KafkaProducer as KProd # type: ignore[import-not-found]
|
|
12
|
+
from kafka import KafkaConsumer as KCons # type: ignore[import-not-found]
|
|
13
|
+
|
|
12
14
|
except ImportError:
|
|
13
15
|
kafka_available = False
|
|
14
16
|
|
|
@@ -36,14 +38,24 @@ def get_diaspora_config(extra_configs: Dict[str, Any] = {}) -> Dict[str, Any]:
|
|
|
36
38
|
|
|
37
39
|
|
|
38
40
|
if kafka_available:
|
|
39
|
-
|
|
40
|
-
class KafkaProducer(KafkaProducer):
|
|
41
|
+
class KafkaProducer(KProd):
|
|
41
42
|
def __init__(self, **configs):
|
|
42
43
|
configs.setdefault(
|
|
43
44
|
"value_serializer", lambda v: json.dumps(v).encode("utf-8")
|
|
44
45
|
)
|
|
45
46
|
super().__init__(**get_diaspora_config(configs))
|
|
46
47
|
|
|
47
|
-
class KafkaConsumer(
|
|
48
|
+
class KafkaConsumer(KCons):
|
|
48
49
|
def __init__(self, *topics, **configs):
|
|
49
50
|
super().__init__(*topics, **get_diaspora_config(configs))
|
|
51
|
+
else:
|
|
52
|
+
# Create dummy classes that issue a warning when instantiated
|
|
53
|
+
class KafkaProducer: # type: ignore[no-redef]
|
|
54
|
+
def __init__(self, *args, **kwargs):
|
|
55
|
+
warnings.warn(
|
|
56
|
+
"KafkaProducer is not available. Initialization is a no-op.", RuntimeWarning)
|
|
57
|
+
|
|
58
|
+
class KafkaConsumer: # type: ignore[no-redef]
|
|
59
|
+
def __init__(self, *args, **kwargs):
|
|
60
|
+
warnings.warn(
|
|
61
|
+
"KafkaConsumer is not available. Initialization is a no-op.", RuntimeWarning)
|
|
@@ -25,13 +25,13 @@ class WebClient(globus_sdk.BaseClient):
|
|
|
25
25
|
self.user_app_name = app_name
|
|
26
26
|
|
|
27
27
|
def create_key(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
|
|
28
|
-
return self.post("/v1/create_key", headers={"Subject": subject})
|
|
28
|
+
return self.post("/v1/create_key", headers={"Subject": str(subject)})
|
|
29
29
|
|
|
30
30
|
def list_topics(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
|
|
31
|
-
return self.get("/v1/list_topics", headers={"Subject": subject})
|
|
31
|
+
return self.get("/v1/list_topics", headers={"Subject": str(subject)})
|
|
32
32
|
|
|
33
33
|
def register_topic(self, subject: UUID_LIKE_T, topic: str) -> globus_sdk.GlobusHTTPResponse:
|
|
34
|
-
return self.post("/v1/register_topic", headers={"Subject": subject, "Topic": topic})
|
|
34
|
+
return self.post("/v1/register_topic", headers={"Subject": str(subject), "Topic": topic})
|
|
35
35
|
|
|
36
36
|
def unregister_topic(self, subject: UUID_LIKE_T, topic: str) -> globus_sdk.GlobusHTTPResponse:
|
|
37
|
-
return self.post("/v1/unregister_topic", headers={"Subject": subject, "Topic": topic})
|
|
37
|
+
return self.post("/v1/unregister_topic", headers={"Subject": str(subject), "Topic": topic})
|
diaspora_event_sdk/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.0.
|
|
1
|
+
__version__ = "0.0.16"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: diaspora-event-sdk
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.16
|
|
4
4
|
Summary: SDK of Diaspora Event Fabric: Resilience-enabling services for science from HPC to edge
|
|
5
5
|
Home-page: https://github.com/globus-labs/diaspora-event-sdk
|
|
6
6
|
License: LICENSE
|
|
@@ -138,3 +138,21 @@ It seems that you ran `pip install diaspora-event-sdk` to install the Diaspora E
|
|
|
138
138
|
|
|
139
139
|
### kafka.errors.NoBrokersAvailable and kafka.errors.NodeNotReadyError
|
|
140
140
|
These messages might pop up if `create_key` is called shortly before instanciating a Kafka client. This is because there's a delay for AWS Secret Manager to associate the newly generated credential with MSK. Note that `create_key` is called internally by `kafka_client.py` the first time you create one of these clients. Please wait a while (around 1 minute) and retry.
|
|
141
|
+
|
|
142
|
+
### kafka.errors.KafkaTimeoutError: KafkaTimeoutError: Failed to update metadata after 60.0 secs.
|
|
143
|
+
**Step 1: Verify Topic Creation and Access:**
|
|
144
|
+
Before interacting with the producer/consumer, ensure that the topic has been successfully created and access is granted to you. Execute the following command:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from diaspora_event_sdk import Client as GlobusClient
|
|
148
|
+
c = GlobusClient()
|
|
149
|
+
# topic = <the topic you want to use>
|
|
150
|
+
print(c.register_topic(topic))
|
|
151
|
+
```
|
|
152
|
+
This should return a `status: no-op` message, indicating that the topic is already registered and accessible.
|
|
153
|
+
|
|
154
|
+
**Step 2: Wait Automatic Key Creation in KafkaProducer and KafkaConsumer**
|
|
155
|
+
`KafkaProducer` and `KafkaConsumer` would internally call `create_key` if the keys are not found locally (e.g., when you first authenticated with Globus). Behind the sence, the middle service contacts AWS to initialize the asynchronous process of creating and associating the secret. Please wait a while (around 1 minute) and retry.
|
|
156
|
+
|
|
157
|
+
### ssl.SSLCertVerificationError
|
|
158
|
+
This is commmon on MacOS system, see [this StackOverflow answer](https://stackoverflow.com/a/53310545).
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
diaspora_event_sdk/__init__.py,sha256=
|
|
2
|
-
diaspora_event_sdk/version.py,sha256=
|
|
1
|
+
diaspora_event_sdk/__init__.py,sha256=ZMeMHPNphLB61jBsOO6kCRUtCWYBtvvNvl1QhwHvrqI,399
|
|
2
|
+
diaspora_event_sdk/version.py,sha256=8ss7zPyQ3YfaQJw9IIGX35NOL7ASZ7-LErAxKnQDN7c,23
|
|
3
3
|
diaspora_event_sdk/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
diaspora_event_sdk/sdk/_environments.py,sha256=UwzEeBVRuP7ZyqTJagNVA24EiCLuOYiy395q608AwQ0,358
|
|
5
|
-
diaspora_event_sdk/sdk/client.py,sha256=
|
|
5
|
+
diaspora_event_sdk/sdk/client.py,sha256=2KMUdnOEtSe1uV3rpRZHd5tLOFgC_fztzny1XxL4rh8,3496
|
|
6
6
|
diaspora_event_sdk/sdk/decorators.py,sha256=Gel8AyhIjbf4-FNintTNcOqvC9hHH_YwbOH257Nfmf0,884
|
|
7
|
-
diaspora_event_sdk/sdk/kafka_client.py,sha256=
|
|
8
|
-
diaspora_event_sdk/sdk/web_client.py,sha256=
|
|
7
|
+
diaspora_event_sdk/sdk/kafka_client.py,sha256=uibvx41bpHP4dv-A2trmJKb2oovzH4FQ6FUBLC9DLlg,2122
|
|
8
|
+
diaspora_event_sdk/sdk/web_client.py,sha256=RCXZhMtPBe53c4-MmyBiFIdqsmcTOnV3ashaaQHKMew,1325
|
|
9
9
|
diaspora_event_sdk/sdk/login_manager/__init__.py,sha256=yeqVgjeHLMX0WZJu2feJmq-fbeXvSxWghVV81ygfY-w,239
|
|
10
10
|
diaspora_event_sdk/sdk/login_manager/client_login.py,sha256=gvR4PkIqQpIywNieJQ_u11PHUmdLxQ0Ho-QgPSfu8bw,1798
|
|
11
11
|
diaspora_event_sdk/sdk/login_manager/decorators.py,sha256=EFEp71d0oJ7vo2H8W7DJ2gPrDfGzeNXUNxri1C0l8h0,1047
|
|
@@ -17,8 +17,9 @@ diaspora_event_sdk/sdk/login_manager/tokenstore.py,sha256=7jRm01rzsbvniaCfYtDDWE
|
|
|
17
17
|
diaspora_event_sdk/sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
18
|
diaspora_event_sdk/sdk/utils/uuid_like.py,sha256=xbxf0YXpDhdii16lwPLWRN21qFekHrNrqODSToMPtCg,470
|
|
19
19
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
|
|
21
|
-
diaspora_event_sdk-0.0.
|
|
22
|
-
diaspora_event_sdk-0.0.
|
|
23
|
-
diaspora_event_sdk-0.0.
|
|
24
|
-
diaspora_event_sdk-0.0.
|
|
20
|
+
tests/unit/test_client.py,sha256=KjqXW7Mm3fm17lqhYdixL8s50z83F6g7MGKcOXJIyFc,2635
|
|
21
|
+
diaspora_event_sdk-0.0.16.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
22
|
+
diaspora_event_sdk-0.0.16.dist-info/METADATA,sha256=Hf_rvVt37hJn-iunRpkYO0pxP_gtJl_GbWdmXvTposs,8181
|
|
23
|
+
diaspora_event_sdk-0.0.16.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
24
|
+
diaspora_event_sdk-0.0.16.dist-info/top_level.txt,sha256=OVun-67t3fkLFEIwvJuNINgFFvAc--bClYhXjLhMmvs,25
|
|
25
|
+
diaspora_event_sdk-0.0.16.dist-info/RECORD,,
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from unittest.mock import Mock, patch
|
|
3
|
+
from unittest.mock import MagicMock
|
|
4
|
+
from diaspora_event_sdk import Client
|
|
5
|
+
from diaspora_event_sdk.sdk.web_client import WebClient
|
|
6
|
+
from diaspora_event_sdk.sdk.login_manager import LoginManager
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def mock_login_manager(): # TODO
|
|
10
|
+
login_manager = Mock(spec=LoginManager())
|
|
11
|
+
login_manager.get_web_client.return_value = Mock(spec=WebClient)
|
|
12
|
+
login_manager.get_auth_client.return_value = Mock(
|
|
13
|
+
oauth2_userinfo=lambda: {"sub": "test_sub"})
|
|
14
|
+
login_manager._token_storage.get_token_data.return_value = {
|
|
15
|
+
'access_key': 'test_access', 'secret_key': 'test_secret'}
|
|
16
|
+
login_manager.get_web_client.return_value.create_key.return_value = {
|
|
17
|
+
"status": "success", "access_key": "new_access", "secret_key": "new_secret"}
|
|
18
|
+
|
|
19
|
+
# Use MagicMock for _access_lock
|
|
20
|
+
login_manager._access_lock = MagicMock()
|
|
21
|
+
|
|
22
|
+
return login_manager
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def client(mock_login_manager):
|
|
27
|
+
return Client(login_manager=mock_login_manager)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_init(mock_login_manager):
|
|
31
|
+
client = Client(login_manager=mock_login_manager)
|
|
32
|
+
assert client.login_manager == mock_login_manager
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_logout(client):
|
|
36
|
+
assert not client.login_manager.logout.called, "Verify test setup"
|
|
37
|
+
client.logout()
|
|
38
|
+
assert client.login_manager.logout.called
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_create_key(client):
|
|
42
|
+
result = client.create_key()
|
|
43
|
+
assert result == {"username": "test_sub", "password": "new_secret"}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_retrieve_key_existing(client):
|
|
47
|
+
result = client.retrieve_key()
|
|
48
|
+
assert result == {"username": "test_sub", "password": "test_secret"}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_retrieve_key_missing(client, mock_login_manager):
|
|
52
|
+
# Set up side_effect for get_token_data: None on first call, token data on the next
|
|
53
|
+
# the second call returns scope, resource_server, access_token, refresh_token, etc.
|
|
54
|
+
mock_login_manager._token_storage.get_token_data.side_effect = [
|
|
55
|
+
None,
|
|
56
|
+
{'scope': 'scope', 'resource_server': 'resource_server'}
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
# should internally call create_key
|
|
60
|
+
result = client.retrieve_key()
|
|
61
|
+
|
|
62
|
+
assert result["username"] == "test_sub"
|
|
63
|
+
assert result["password"] == "new_secret"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_list_topics(client):
|
|
67
|
+
client.list_topics()
|
|
68
|
+
client.web_client.list_topics.assert_called_with("test_sub")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_register_topic(client):
|
|
72
|
+
topic = "test_topic"
|
|
73
|
+
client.register_topic(topic)
|
|
74
|
+
client.web_client.register_topic.assert_called_with("test_sub", topic)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_unregister_topic(client):
|
|
78
|
+
topic = "test_topic"
|
|
79
|
+
client.unregister_topic(topic)
|
|
80
|
+
client.web_client.unregister_topic.assert_called_with("test_sub", topic)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|