diaspora-event-sdk 0.4.1__tar.gz → 0.4.3__tar.gz

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.
Files changed (47) hide show
  1. {diaspora-event-sdk-0.4.1/diaspora_event_sdk.egg-info → diaspora_event_sdk-0.4.3}/PKG-INFO +21 -3
  2. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/__init__.py +5 -2
  3. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/_environments.py +0 -2
  4. diaspora_event_sdk-0.4.3/diaspora_event_sdk/sdk/client.py +113 -0
  5. diaspora_event_sdk-0.4.3/diaspora_event_sdk/sdk/kafka_client.py +182 -0
  6. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/login_manager/manager.py +60 -1
  7. diaspora_event_sdk-0.4.3/diaspora_event_sdk/sdk/web_client.py +70 -0
  8. diaspora_event_sdk-0.4.3/diaspora_event_sdk/version.py +1 -0
  9. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3/diaspora_event_sdk.egg-info}/PKG-INFO +21 -3
  10. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk.egg-info/SOURCES.txt +1 -2
  11. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk.egg-info/requires.txt +1 -1
  12. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/setup.py +1 -1
  13. diaspora_event_sdk-0.4.3/tests/unit/apis_test.py +289 -0
  14. diaspora-event-sdk-0.4.1/diaspora_event_sdk/sdk/client.py +0 -244
  15. diaspora-event-sdk-0.4.1/diaspora_event_sdk/sdk/kafka_client.py +0 -138
  16. diaspora-event-sdk-0.4.1/diaspora_event_sdk/sdk/web_client.py +0 -155
  17. diaspora-event-sdk-0.4.1/diaspora_event_sdk/version.py +0 -1
  18. diaspora-event-sdk-0.4.1/tests/unit/apis_test.py +0 -136
  19. diaspora-event-sdk-0.4.1/tests/unit/client_test.py +0 -100
  20. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/LICENSE +0 -0
  21. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/MANIFEST.in +0 -0
  22. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/README.md +0 -0
  23. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/__init__.py +0 -0
  24. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/aws_iam_msk.py +0 -0
  25. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/botocore/__init__.py +0 -0
  26. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/botocore/auth.py +0 -0
  27. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/botocore/awsrequest.py +0 -0
  28. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/botocore/compat.py +0 -0
  29. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/botocore/credentials.py +0 -0
  30. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/botocore/exceptions.py +0 -0
  31. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/botocore/utils.py +0 -0
  32. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/decorators.py +0 -0
  33. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/login_manager/__init__.py +0 -0
  34. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/login_manager/client_login.py +0 -0
  35. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/login_manager/decorators.py +0 -0
  36. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/login_manager/globus_auth.py +0 -0
  37. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/login_manager/login_flow.py +0 -0
  38. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/login_manager/protocol.py +0 -0
  39. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/login_manager/tokenstore.py +0 -0
  40. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/utils/__init__.py +0 -0
  41. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk/sdk/utils/uuid_like.py +0 -0
  42. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk.egg-info/dependency_links.txt +0 -0
  43. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/diaspora_event_sdk.egg-info/top_level.txt +0 -0
  44. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/mypy.ini +0 -0
  45. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/setup.cfg +0 -0
  46. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/tests/__init__.py +0 -0
  47. {diaspora-event-sdk-0.4.1 → diaspora_event_sdk-0.4.3}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: diaspora-event-sdk
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Diaspora Event Fabric SDK
5
5
  Home-page: https://github.com/globus-labs/diaspora-event-sdk
6
6
  License: Apache 2.0
@@ -15,9 +15,27 @@ Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: globus-sdk<4,>=3.59.0
18
20
  Provides-Extra: kafka-python
21
+ Requires-Dist: kafka-python; extra == "kafka-python"
19
22
  Provides-Extra: test
20
- License-File: LICENSE
23
+ Requires-Dist: pytest; extra == "test"
24
+ Requires-Dist: pytest-cov; extra == "test"
25
+ Requires-Dist: coverage; extra == "test"
26
+ Requires-Dist: mypy; extra == "test"
27
+ Requires-Dist: tox; extra == "test"
28
+ Requires-Dist: check-manifest; extra == "test"
29
+ Requires-Dist: pre-commit; extra == "test"
30
+ Dynamic: classifier
31
+ Dynamic: description
32
+ Dynamic: description-content-type
33
+ Dynamic: home-page
34
+ Dynamic: license
35
+ Dynamic: license-file
36
+ Dynamic: provides-extra
37
+ Dynamic: requires-dist
38
+ Dynamic: summary
21
39
 
22
40
  # Diaspora Event Fabric SDK
23
41
 
@@ -9,7 +9,10 @@ from diaspora_event_sdk.sdk.client import Client
9
9
  from diaspora_event_sdk.sdk.kafka_client import (
10
10
  KafkaProducer,
11
11
  KafkaConsumer,
12
- block_until_ready,
13
12
  )
14
13
 
15
- __all__ = ("Client", "KafkaProducer", "KafkaConsumer", "block_until_ready")
14
+ __all__ = (
15
+ "Client",
16
+ "KafkaProducer",
17
+ "KafkaConsumer",
18
+ )
@@ -13,9 +13,7 @@ def get_web_service_url(envname: Union[str, None] = None) -> str:
13
13
  env = envname or _get_envname()
14
14
  urls = {
15
15
  "production": "https://diaspora-web-service.qpp943wkvr7b2.us-east-1.cs.amazonlightsail.com/",
16
- "dev": "https://diaspora-web-service.qpp943wkvr7b2.us-east-1.cs.amazonlightsail.com/",
17
16
  "local": "http://localhost:8000",
18
- "legacy": "https://diaspora-web-service-dev.ml22sevubfnks.us-east-1.cs.amazonlightsail.com",
19
17
  }
20
18
 
21
19
  return urls.get(env, urls["production"])
@@ -0,0 +1,113 @@
1
+ from typing import Optional
2
+
3
+ from diaspora_event_sdk.sdk.login_manager import (
4
+ LoginManager,
5
+ LoginManagerProtocol,
6
+ requires_login,
7
+ )
8
+
9
+ from ._environments import get_web_service_url
10
+
11
+
12
+ class Client:
13
+ def __init__(
14
+ self,
15
+ environment: Optional[str] = None,
16
+ login_manager: Optional[LoginManagerProtocol] = None,
17
+ ):
18
+ self.web_service_address = get_web_service_url(environment)
19
+
20
+ # if a login manager was passed, no login flow is triggered
21
+ if login_manager is not None:
22
+ self.login_manager: LoginManagerProtocol = login_manager
23
+ # but if login handling is implicit (as when no login manager is passed)
24
+ # then ensure that the user is logged in
25
+ else:
26
+ self.login_manager = LoginManager(environment=environment)
27
+ self.login_manager.ensure_logged_in()
28
+
29
+ self.web_client = self.login_manager.get_web_client(
30
+ base_url=self.web_service_address
31
+ )
32
+ self.auth_client = self.login_manager.get_auth_client()
33
+ self.subject_openid = self.auth_client.userinfo()["sub"]
34
+ self.namespace = f"ns-{self.subject_openid.replace('-', '')[-12:]}"
35
+
36
+ def logout(self):
37
+ """Remove credentials from your local system"""
38
+ self.login_manager.logout()
39
+
40
+ @requires_login
41
+ def create_user(self):
42
+ """
43
+ Create an IAM user with policy and namespace for the current user (POST /api/v3/user).
44
+ Returns status, message, subject, and namespace.
45
+ """
46
+ resp = self.web_client.create_user(self.subject_openid)
47
+ return resp.data if hasattr(resp, "data") else resp
48
+
49
+ @requires_login
50
+ def delete_user(self):
51
+ """
52
+ Delete the IAM user and all associated resources for the current user (DELETE /api/v3/user).
53
+ Returns status and message.
54
+ """
55
+ resp = self.web_client.delete_user(self.subject_openid)
56
+ return resp.data if hasattr(resp, "data") else resp
57
+
58
+ @requires_login
59
+ def create_key(self):
60
+ """
61
+ Create a new access key for the current user (POST /api/v3/key).
62
+ This will replace any existing access key (force refresh).
63
+ Returns status, message, access_key, secret_key, and create_date.
64
+ """
65
+ resp = self.web_client.create_key(self.subject_openid)
66
+ return resp.data if hasattr(resp, "data") else resp
67
+
68
+ @requires_login
69
+ def delete_key(self):
70
+ """
71
+ Delete access keys from IAM and DynamoDB for the current user (DELETE /api/v3/key).
72
+ Returns status and message.
73
+ """
74
+ resp = self.web_client.delete_key(self.subject_openid)
75
+ return resp.data if hasattr(resp, "data") else resp
76
+
77
+ @requires_login
78
+ def list_namespaces(self):
79
+ """
80
+ List all namespaces owned by the current user and their topics (GET /api/v3/namespace).
81
+ Returns status, message, and namespaces dict (namespace -> list of topics).
82
+ """
83
+ resp = self.web_client.list_namespaces(self.subject_openid)
84
+ return resp.data if hasattr(resp, "data") else resp
85
+
86
+ @requires_login
87
+ def create_topic(self, topic: str):
88
+ """
89
+ Create a topic under the user's default namespace (POST /api/v3/{namespace}/{topic}).
90
+ Returns status, message, and topics list.
91
+ """
92
+ resp = self.web_client.create_topic(self.subject_openid, self.namespace, topic)
93
+ return resp.data if hasattr(resp, "data") else resp
94
+
95
+ @requires_login
96
+ def delete_topic(self, topic: str):
97
+ """
98
+ Delete a topic from the user's default namespace (DELETE /api/v3/{namespace}/{topic}).
99
+ Returns status, message, and topics list.
100
+ """
101
+ resp = self.web_client.delete_topic(self.subject_openid, self.namespace, topic)
102
+ return resp.data if hasattr(resp, "data") else resp
103
+
104
+ @requires_login
105
+ def recreate_topic(self, topic: str):
106
+ """
107
+ Recreate a topic in the user's default namespace by deleting and recreating it (PUT /api/v3/{namespace}/{topic}/recreate).
108
+ Returns status and message.
109
+ """
110
+ resp = self.web_client.recreate_topic(
111
+ self.subject_openid, self.namespace, topic
112
+ )
113
+ return resp.data if hasattr(resp, "data") else resp
@@ -0,0 +1,182 @@
1
+ import json
2
+ import logging
3
+ import time
4
+ import uuid
5
+ import warnings
6
+ from typing import Any, Dict
7
+
8
+ from .aws_iam_msk import generate_auth_token
9
+ from .client import Client
10
+
11
+ # File-level logger
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # If kafka-python is not installed, Kafka functionality is not available through diaspora-event-sdk.
15
+ kafka_available = True
16
+ try:
17
+ import os
18
+
19
+ from kafka import KafkaConsumer as KCons # type: ignore[import,import-not-found]
20
+ from kafka import KafkaProducer as KProd # type: ignore[import,import-not-found]
21
+ from kafka.errors import KafkaTimeoutError, TopicAuthorizationFailedError # type: ignore[import,import-not-found]
22
+ from kafka.sasl.oauth import (
23
+ AbstractTokenProvider, # type: ignore[import,import-not-found]
24
+ )
25
+
26
+ class MSKTokenProvider(AbstractTokenProvider):
27
+ def token(self):
28
+ token, _ = generate_auth_token("us-east-1")
29
+ return token
30
+ except Exception:
31
+ kafka_available = False
32
+ # Fallback if kafka-python is not available
33
+ TopicAuthorizationFailedError = Exception
34
+ KafkaTimeoutError = Exception
35
+
36
+
37
+ def get_diaspora_config(extra_configs: Dict[str, Any] = {}) -> Dict[str, Any]:
38
+ """
39
+ Retrieve default Diaspora event fabric connection configurations for Kafka clients.
40
+ Merges default configurations with custom ones provided.
41
+ """
42
+
43
+ try:
44
+ client = Client()
45
+ keys = client.create_key() # create or retrieve key
46
+ os.environ["OCTOPUS_AWS_ACCESS_KEY_ID"] = keys["access_key"]
47
+ os.environ["OCTOPUS_AWS_SECRET_ACCESS_KEY"] = keys["secret_key"]
48
+ os.environ["OCTOPUS_BOOTSTRAP_SERVERS"] = keys["endpoint"]
49
+
50
+ except Exception as e:
51
+ raise RuntimeError("Failed to retrieve Kafka keys") from e
52
+
53
+ conf = {
54
+ "bootstrap_servers": os.environ["OCTOPUS_BOOTSTRAP_SERVERS"],
55
+ "security_protocol": "SASL_SSL",
56
+ "sasl_mechanism": "OAUTHBEARER",
57
+ "api_version": (3, 8, 1),
58
+ "sasl_oauth_token_provider": MSKTokenProvider(),
59
+ }
60
+ conf.update(extra_configs)
61
+ return conf
62
+
63
+
64
+ if kafka_available:
65
+
66
+ class KafkaProducer(KProd):
67
+ """
68
+ Wrapper around KProd that:
69
+ - Requires at least one topic
70
+ - Sets a default JSON serializer
71
+ - Does NOT block until topics have partition metadata
72
+ """
73
+
74
+ def __init__(self, *topics, **configs):
75
+ if not topics:
76
+ raise ValueError("KafkaProducer requires at least one topic")
77
+ self.topics = topics
78
+
79
+ configs.setdefault(
80
+ "value_serializer",
81
+ lambda v: json.dumps(v).encode("utf-8"),
82
+ )
83
+
84
+ super().__init__(**get_diaspora_config(configs))
85
+ # Note: We do NOT block on metadata here
86
+
87
+ class KafkaConsumer(KCons):
88
+ def __init__(self, *topics, **configs):
89
+ if not topics:
90
+ raise ValueError("KafkaConsumer requires at least one topic")
91
+ self.topics = topics
92
+
93
+ super().__init__(*topics, **get_diaspora_config(configs))
94
+ # Note: We do NOT block on metadata here
95
+
96
+
97
+ else:
98
+ # Create dummy classes that issue a warning when instantiated
99
+ class KafkaProducer: # type: ignore[no-redef]
100
+ def __init__(self, *args, **kwargs):
101
+ warnings.warn(
102
+ "KafkaProducer is not available. Initialization is a no-op.",
103
+ RuntimeWarning,
104
+ )
105
+
106
+ class KafkaConsumer: # type: ignore[no-redef]
107
+ def __init__(self, *args, **kwargs):
108
+ warnings.warn(
109
+ "KafkaConsumer is not available. Initialization is a no-op.",
110
+ RuntimeWarning,
111
+ )
112
+
113
+
114
+ def reliable_client_creation() -> str:
115
+ """
116
+ Reliably create a client and test topic operations with retry logic.
117
+
118
+ Returns:
119
+ str: The full topic name in format "namespace.topic-name"
120
+ """
121
+ attempt = 0
122
+ while True:
123
+ attempt += 1
124
+ if attempt > 1:
125
+ time.sleep(5)
126
+
127
+ topic_name = None
128
+ kafka_topic = None
129
+ client = None
130
+ try:
131
+ client = Client()
132
+ key_result = client.create_key()
133
+
134
+ # If status is not success, throw exception
135
+ if key_result.get("status") != "success":
136
+ raise RuntimeError(
137
+ f"Failed to create key: {key_result.get('message', 'Unknown error')}"
138
+ )
139
+
140
+ # If key is fresh (just created), wait for IAM policy to propagate
141
+ if key_result.get("fresh", False):
142
+ time.sleep(8)
143
+
144
+ topic_name = f"topic-{str(uuid.uuid4())[:5]}"
145
+ kafka_topic = f"{client.namespace}.{topic_name}"
146
+
147
+ # If status is not success, throw exception
148
+ topic_result = client.create_topic(topic_name)
149
+ if topic_result.get("status") != "success":
150
+ raise RuntimeError(
151
+ f"Failed to create topic: {topic_result.get('message', 'Unknown error')}"
152
+ )
153
+ time.sleep(3) # Wait after topic creation before produce
154
+
155
+ producer = KafkaProducer(kafka_topic)
156
+ for i in range(3):
157
+ future = producer.send(
158
+ kafka_topic, {"message_id": i + 1, "content": f"Message {i + 1}"}
159
+ )
160
+ future.get(timeout=30)
161
+ producer.close()
162
+ time.sleep(2) # Wait for the produced messages to be consumed
163
+
164
+ consumer = KafkaConsumer(kafka_topic, auto_offset_reset="earliest")
165
+ consumer.poll(timeout_ms=10000)
166
+ consumer.close()
167
+
168
+ client.delete_topic(topic_name)
169
+ return kafka_topic
170
+ except Exception as e:
171
+ logger.info(f"Error in attempt {attempt}: {type(e).__name__}: {str(e)}")
172
+ if client:
173
+ try:
174
+ if topic_name:
175
+ client.delete_topic(topic_name)
176
+ except Exception:
177
+ pass
178
+ try:
179
+ client.delete_user()
180
+ except Exception:
181
+ pass
182
+ continue
@@ -19,6 +19,61 @@ from .login_flow import do_link_auth_flow
19
19
  log = logging.getLogger(__name__)
20
20
 
21
21
 
22
+ class FilteredClientCredentialsAuthorizer(globus_sdk.ClientCredentialsAuthorizer):
23
+ """
24
+ A custom ClientCredentialsAuthorizer that filters token responses to only
25
+ include tokens for a specific resource server.
26
+
27
+ This is needed when client credentials return tokens for multiple resource
28
+ servers, but ClientCredentialsAuthorizer expects exactly one token.
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ confidential_client: globus_sdk.ConfidentialAppAuthClient,
34
+ scopes: list[str],
35
+ *,
36
+ resource_server: str,
37
+ access_token: str | None = None,
38
+ expires_at: int | None = None,
39
+ on_refresh: t.Callable[[globus_sdk.OAuthTokenResponse], None] | None = None,
40
+ ) -> None:
41
+ self._target_resource_server = resource_server
42
+ # Store the original on_refresh callback
43
+ self._original_on_refresh = on_refresh
44
+ super().__init__(
45
+ confidential_client=confidential_client,
46
+ scopes=scopes,
47
+ access_token=access_token,
48
+ expires_at=expires_at,
49
+ on_refresh=self._filtered_on_refresh,
50
+ )
51
+
52
+ def _extract_token_data(
53
+ self, res: globus_sdk.OAuthClientCredentialsResponse
54
+ ) -> dict[str, t.Any]:
55
+ """
56
+ Extract token data, filtering to only the target resource server.
57
+ """
58
+ token_data = res.by_resource_server
59
+ if self._target_resource_server in token_data:
60
+ # Return only the token for the target resource server
61
+ return token_data[self._target_resource_server]
62
+ else:
63
+ raise ValueError(
64
+ f"Token response does not contain token for {self._target_resource_server}"
65
+ )
66
+
67
+ def _filtered_on_refresh(
68
+ self, token_response: globus_sdk.OAuthTokenResponse
69
+ ) -> None:
70
+ """
71
+ Call the original on_refresh callback with the filtered token response.
72
+ """
73
+ if self._original_on_refresh:
74
+ self._original_on_refresh(token_response)
75
+
76
+
22
77
  def _get_diaspora_all_scope() -> str:
23
78
  return os.getenv(
24
79
  "DIASPORA_SCOPE",
@@ -153,9 +208,13 @@ class LoginManager:
153
208
  expires_at = tokens["expires_at_seconds"]
154
209
 
155
210
  with self._access_lock:
156
- return globus_sdk.ClientCredentialsAuthorizer(
211
+ # Use a custom authorizer that filters token responses to only
212
+ # the requested resource server, handling cases where client
213
+ # credentials return tokens for multiple resource servers
214
+ return FilteredClientCredentialsAuthorizer(
157
215
  confidential_client=get_client_login(),
158
216
  scopes=scopes,
217
+ resource_server=resource_server,
159
218
  access_token=access_token,
160
219
  expires_at=expires_at,
161
220
  on_refresh=self._token_storage.on_refresh,
@@ -0,0 +1,70 @@
1
+ from typing import Optional
2
+
3
+ import globus_sdk
4
+
5
+ from diaspora_event_sdk.sdk.utils.uuid_like import UUID_LIKE_T
6
+
7
+ from ._environments import get_web_service_url
8
+
9
+
10
+ class WebClient(globus_sdk.BaseClient):
11
+ def __init__(
12
+ self,
13
+ *,
14
+ environment: Optional[str] = None,
15
+ base_url: Optional[str] = None,
16
+ app_name: Optional[str] = None,
17
+ **kwargs,
18
+ ):
19
+ if base_url is None:
20
+ base_url = get_web_service_url(environment)
21
+
22
+ super().__init__(environment=environment, base_url=base_url, **kwargs)
23
+
24
+ self._user_app_name = None
25
+ self.user_app_name = app_name
26
+
27
+ def create_user(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
28
+ """Call the v3 create_user endpoint (POST /api/v3/user)."""
29
+ return self.post("/api/v3/user", headers={"Subject": str(subject)})
30
+
31
+ def delete_user(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
32
+ """Call the v3 delete_user endpoint (DELETE /api/v3/user)."""
33
+ return self.delete("/api/v3/user", headers={"Subject": str(subject)})
34
+
35
+ def create_key(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
36
+ """Call the v3 create_key endpoint (POST /api/v3/key)."""
37
+ return self.post("/api/v3/key", headers={"Subject": str(subject)})
38
+
39
+ def delete_key(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
40
+ """Call the v3 delete_key endpoint (DELETE /api/v3/key)."""
41
+ return self.delete("/api/v3/key", headers={"Subject": str(subject)})
42
+
43
+ def list_namespaces(self, subject: UUID_LIKE_T) -> globus_sdk.GlobusHTTPResponse:
44
+ """Call the v3 list_namespaces endpoint (GET /api/v3/namespace)."""
45
+ return self.get("/api/v3/namespace", headers={"Subject": str(subject)})
46
+
47
+ def create_topic(
48
+ self, subject: UUID_LIKE_T, namespace: str, topic: str
49
+ ) -> globus_sdk.GlobusHTTPResponse:
50
+ """Call the v3 create_topic endpoint (POST /api/v3/{namespace}/{topic})."""
51
+ return self.post(
52
+ f"/api/v3/{namespace}/{topic}", headers={"Subject": str(subject)}
53
+ )
54
+
55
+ def delete_topic(
56
+ self, subject: UUID_LIKE_T, namespace: str, topic: str
57
+ ) -> globus_sdk.GlobusHTTPResponse:
58
+ """Call the v3 delete_topic endpoint (DELETE /api/v3/{namespace}/{topic})."""
59
+ return self.delete(
60
+ f"/api/v3/{namespace}/{topic}", headers={"Subject": str(subject)}
61
+ )
62
+
63
+ def recreate_topic(
64
+ self, subject: UUID_LIKE_T, namespace: str, topic: str
65
+ ) -> globus_sdk.GlobusHTTPResponse:
66
+ """Call the v3 recreate_topic endpoint (PUT /api/v3/{namespace}/{topic}/recreate)."""
67
+ return self.put(
68
+ f"/api/v3/{namespace}/{topic}/recreate",
69
+ headers={"Subject": str(subject)},
70
+ )
@@ -0,0 +1 @@
1
+ __version__ = "0.4.3"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: diaspora-event-sdk
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Diaspora Event Fabric SDK
5
5
  Home-page: https://github.com/globus-labs/diaspora-event-sdk
6
6
  License: Apache 2.0
@@ -15,9 +15,27 @@ Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: globus-sdk<4,>=3.59.0
18
20
  Provides-Extra: kafka-python
21
+ Requires-Dist: kafka-python; extra == "kafka-python"
19
22
  Provides-Extra: test
20
- License-File: LICENSE
23
+ Requires-Dist: pytest; extra == "test"
24
+ Requires-Dist: pytest-cov; extra == "test"
25
+ Requires-Dist: coverage; extra == "test"
26
+ Requires-Dist: mypy; extra == "test"
27
+ Requires-Dist: tox; extra == "test"
28
+ Requires-Dist: check-manifest; extra == "test"
29
+ Requires-Dist: pre-commit; extra == "test"
30
+ Dynamic: classifier
31
+ Dynamic: description
32
+ Dynamic: description-content-type
33
+ Dynamic: home-page
34
+ Dynamic: license
35
+ Dynamic: license-file
36
+ Dynamic: provides-extra
37
+ Dynamic: requires-dist
38
+ Dynamic: summary
21
39
 
22
40
  # Diaspora Event Fabric SDK
23
41
 
@@ -36,5 +36,4 @@ diaspora_event_sdk/sdk/login_manager/tokenstore.py
36
36
  diaspora_event_sdk/sdk/utils/__init__.py
37
37
  diaspora_event_sdk/sdk/utils/uuid_like.py
38
38
  tests/__init__.py
39
- tests/unit/apis_test.py
40
- tests/unit/client_test.py
39
+ tests/unit/apis_test.py
@@ -1,4 +1,4 @@
1
- globus-sdk<4,>=3.20.1
1
+ globus-sdk<4,>=3.59.0
2
2
 
3
3
  [kafka-python]
4
4
  kafka-python
@@ -44,7 +44,7 @@ setup(
44
44
  license="Apache 2.0",
45
45
  url="https://github.com/globus-labs/diaspora-event-sdk",
46
46
  install_requires=[
47
- "globus-sdk>=3.20.1,<4",
47
+ "globus-sdk>=3.59.0,<4",
48
48
  ],
49
49
  extras_require={
50
50
  "kafka-python": ["kafka-python"],