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.
@@ -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 # Globus client
10
- from diaspora_event_sdk.sdk.kafka_client import kafka_available
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")
@@ -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 LoginManager, LoginManagerProtocol, requires_login
5
- from ._environments import TOKEN_EXCHANGE, DIASPORA_RESOURCE_SERVER
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
- tokens['access_key'], tokens['secret_key'] = resp['access_key'], resp['secret_key']
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
- (self.login_manager._token_storage.namespace,
52
- DIASPORA_RESOURCE_SERVER,
53
- json.dumps(tokens))
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['secret_key']}
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['secret_key']}
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, KafkaConsumer
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(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})
@@ -1 +1 @@
1
- __version__ = "0.0.13"
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.13
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=HecfCu-giI3bBGXcysQHmzkrX9wbwcXoU4k_uHeJWh0,540
2
- diaspora_event_sdk/version.py,sha256=SxvRes_80c-42zwvn5BXPvpGVs_EZ69FUEtUBF1k9Ts,23
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=DhUDtdkPRmn8P0fTXWg3x-Gn3p2J3ir0Ki7qeOFyp9I,3396
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=bfYKAAbgzRdeMK_gmr84n5653KYboWjO6F4BjgmWExs,1524
8
- diaspora_event_sdk/sdk/web_client.py,sha256=BgRza0fJudvgRCZ_P9BugFp57oYRWekpBf2KlZYFqUY,1305
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
- diaspora_event_sdk-0.0.13.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
21
- diaspora_event_sdk-0.0.13.dist-info/METADATA,sha256=9t_Y0HDrVPAW9R-4BvxuLI-bgmbh768TeoHpuCXt4l4,7061
22
- diaspora_event_sdk-0.0.13.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
23
- diaspora_event_sdk-0.0.13.dist-info/top_level.txt,sha256=OVun-67t3fkLFEIwvJuNINgFFvAc--bClYhXjLhMmvs,25
24
- diaspora_event_sdk-0.0.13.dist-info/RECORD,,
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)