vortex-python-sdk 0.0.1__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.
- vortex_python_sdk-0.0.1.dist-info/METADATA +233 -0
- vortex_python_sdk-0.0.1.dist-info/RECORD +9 -0
- vortex_python_sdk-0.0.1.dist-info/WHEEL +5 -0
- vortex_python_sdk-0.0.1.dist-info/licenses/LICENSE +21 -0
- vortex_python_sdk-0.0.1.dist-info/top_level.txt +1 -0
- vortex_sdk/__init__.py +33 -0
- vortex_sdk/py.typed +0 -0
- vortex_sdk/types.py +59 -0
- vortex_sdk/vortex.py +452 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vortex-python-sdk
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Vortex Python SDK for invitation management and JWT generation
|
|
5
|
+
Author-email: TeamVortexSoftware <support@vortexsoftware.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/teamvortexsoftware/vortex-python-sdk
|
|
8
|
+
Project-URL: Repository, https://github.com/teamvortexsoftware/vortex-python-sdk.git
|
|
9
|
+
Project-URL: Documentation, https://docs.vortexsoftware.com/python-sdk
|
|
10
|
+
Project-URL: Changelog, https://github.com/teamvortexsoftware/vortex-python-sdk/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: vortex,invitations,jwt,api,sdk
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: httpx>=0.27.0
|
|
27
|
+
Requires-Dist: pydantic>=2.8.0
|
|
28
|
+
Requires-Dist: typing-extensions>=4.8.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# Vortex Python SDK
|
|
40
|
+
|
|
41
|
+
A Python SDK for Vortex invitation management and JWT generation.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install vortex-python-sdk
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> **Note**: The package will be available on PyPI once published. See [PUBLISHING.md](PUBLISHING.md) for publishing instructions.
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
### Basic Setup
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from vortex_sdk import Vortex
|
|
57
|
+
|
|
58
|
+
# Initialize the client
|
|
59
|
+
vortex = Vortex(api_key="your-api-key")
|
|
60
|
+
|
|
61
|
+
# Or with custom base URL
|
|
62
|
+
vortex = Vortex(api_key="your-api-key", base_url="https://custom-api.example.com")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### JWT Generation
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# Generate JWT for a user
|
|
69
|
+
jwt = vortex.generate_jwt({
|
|
70
|
+
"user_id": "user123",
|
|
71
|
+
"identifiers": {
|
|
72
|
+
"email": "user@example.com",
|
|
73
|
+
"username": "johndoe"
|
|
74
|
+
},
|
|
75
|
+
"groups": ["admin", "users"],
|
|
76
|
+
"role": "admin"
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
print(f"JWT: {jwt}")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Invitation Management
|
|
83
|
+
|
|
84
|
+
#### Get Invitations by Target
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import asyncio
|
|
88
|
+
|
|
89
|
+
async def get_user_invitations():
|
|
90
|
+
# Async version
|
|
91
|
+
invitations = await vortex.get_invitations_by_target("email", "user@example.com")
|
|
92
|
+
for invitation in invitations:
|
|
93
|
+
print(f"Invitation ID: {invitation.id}, Status: {invitation.status}")
|
|
94
|
+
|
|
95
|
+
# Sync version
|
|
96
|
+
invitations = vortex.get_invitations_by_target_sync("email", "user@example.com")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Accept Invitations
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
async def accept_user_invitations():
|
|
103
|
+
# Async version
|
|
104
|
+
result = await vortex.accept_invitations(
|
|
105
|
+
invitation_ids=["inv1", "inv2"],
|
|
106
|
+
target={"type": "email", "value": "user@example.com"}
|
|
107
|
+
)
|
|
108
|
+
print(f"Result: {result}")
|
|
109
|
+
|
|
110
|
+
# Sync version
|
|
111
|
+
result = vortex.accept_invitations_sync(
|
|
112
|
+
invitation_ids=["inv1", "inv2"],
|
|
113
|
+
target={"type": "email", "value": "user@example.com"}
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Get Specific Invitation
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
async def get_invitation():
|
|
121
|
+
# Async version
|
|
122
|
+
invitation = await vortex.get_invitation("invitation-id")
|
|
123
|
+
print(f"Invitation: {invitation.id}")
|
|
124
|
+
|
|
125
|
+
# Sync version
|
|
126
|
+
invitation = vortex.get_invitation_sync("invitation-id")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### Revoke Invitation
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
async def revoke_invitation():
|
|
133
|
+
# Async version
|
|
134
|
+
result = await vortex.revoke_invitation("invitation-id")
|
|
135
|
+
print(f"Revoked: {result}")
|
|
136
|
+
|
|
137
|
+
# Sync version
|
|
138
|
+
result = vortex.revoke_invitation_sync("invitation-id")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Group Operations
|
|
142
|
+
|
|
143
|
+
#### Get Invitations by Group
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
async def get_group_invitations():
|
|
147
|
+
# Async version
|
|
148
|
+
invitations = await vortex.get_invitations_by_group("organization", "org123")
|
|
149
|
+
print(f"Found {len(invitations)} invitations")
|
|
150
|
+
|
|
151
|
+
# Sync version
|
|
152
|
+
invitations = vortex.get_invitations_by_group_sync("organization", "org123")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Delete Invitations by Group
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
async def delete_group_invitations():
|
|
159
|
+
# Async version
|
|
160
|
+
result = await vortex.delete_invitations_by_group("organization", "org123")
|
|
161
|
+
print(f"Deleted: {result}")
|
|
162
|
+
|
|
163
|
+
# Sync version
|
|
164
|
+
result = vortex.delete_invitations_by_group_sync("organization", "org123")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### Reinvite
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
async def reinvite_user():
|
|
171
|
+
# Async version
|
|
172
|
+
invitation = await vortex.reinvite("invitation-id")
|
|
173
|
+
print(f"Reinvited: {invitation.id}")
|
|
174
|
+
|
|
175
|
+
# Sync version
|
|
176
|
+
invitation = vortex.reinvite_sync("invitation-id")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Context Manager Usage
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
# Async context manager
|
|
183
|
+
async with Vortex(api_key="your-api-key") as vortex:
|
|
184
|
+
invitations = await vortex.get_invitations_by_target("email", "user@example.com")
|
|
185
|
+
|
|
186
|
+
# Sync context manager
|
|
187
|
+
with Vortex(api_key="your-api-key") as vortex:
|
|
188
|
+
invitations = vortex.get_invitations_by_target_sync("email", "user@example.com")
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Error Handling
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from vortex_sdk import VortexApiError
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
invitation = vortex.get_invitation_sync("invalid-id")
|
|
198
|
+
except VortexApiError as e:
|
|
199
|
+
print(f"API Error: {e.message} (Status: {e.status_code})")
|
|
200
|
+
except Exception as e:
|
|
201
|
+
print(f"Unexpected error: {e}")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
### Installation
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Install development dependencies
|
|
210
|
+
pip install -e ".[dev]"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Running Tests
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
pytest
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Code Formatting
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# Format code
|
|
223
|
+
black src/ tests/
|
|
224
|
+
isort src/ tests/
|
|
225
|
+
|
|
226
|
+
# Lint code
|
|
227
|
+
ruff check src/ tests/
|
|
228
|
+
mypy src/
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
vortex_python_sdk-0.0.1.dist-info/licenses/LICENSE,sha256=VndlWxbL4-w3YDf2yE5gJscj4zVXF0qlSq0LDtay3lo,1074
|
|
2
|
+
vortex_sdk/__init__.py,sha256=mFLcyV2i5W7xuj25cThFWYBm6S0HpeOGtUIHDenzhBc,631
|
|
3
|
+
vortex_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
vortex_sdk/types.py,sha256=whBfV7yVcrmk5K7ex1s4FCs92V9_bj-7KUsCRi5fmgk,1491
|
|
5
|
+
vortex_sdk/vortex.py,sha256=_Ov62rzGdLecbID6LVQLMZgDc96xw7jY5qNqsfUjV3o,12963
|
|
6
|
+
vortex_python_sdk-0.0.1.dist-info/METADATA,sha256=BH1AkI5SMi_mhRtttqLa5w1o2gk8ymwdzX3emM6U5iQ,5765
|
|
7
|
+
vortex_python_sdk-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
vortex_python_sdk-0.0.1.dist-info/top_level.txt,sha256=xFDlEcXIIi_sBhkse0YfMnSdg2IlaYUd0oP2UCDc_Y0,11
|
|
9
|
+
vortex_python_sdk-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 TeamVortexSoftware
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vortex_sdk
|
vortex_sdk/__init__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Vortex Python SDK
|
|
3
|
+
|
|
4
|
+
A Python SDK for Vortex invitation management and JWT generation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .vortex import Vortex
|
|
8
|
+
from .types import (
|
|
9
|
+
AuthenticatedUser,
|
|
10
|
+
JwtPayload,
|
|
11
|
+
InvitationTarget,
|
|
12
|
+
Invitation,
|
|
13
|
+
CreateInvitationRequest,
|
|
14
|
+
AcceptInvitationsRequest,
|
|
15
|
+
ApiResponse,
|
|
16
|
+
VortexApiError
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__version__ = "0.0.1"
|
|
20
|
+
__author__ = "TeamVortexSoftware"
|
|
21
|
+
__email__ = "support@vortexsoftware.com"
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"Vortex",
|
|
25
|
+
"AuthenticatedUser",
|
|
26
|
+
"JwtPayload",
|
|
27
|
+
"InvitationTarget",
|
|
28
|
+
"Invitation",
|
|
29
|
+
"CreateInvitationRequest",
|
|
30
|
+
"AcceptInvitationsRequest",
|
|
31
|
+
"ApiResponse",
|
|
32
|
+
"VortexApiError",
|
|
33
|
+
]
|
vortex_sdk/py.typed
ADDED
|
File without changes
|
vortex_sdk/types.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Dict, List, Optional, Union, Literal
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AuthenticatedUser(BaseModel):
|
|
6
|
+
user_id: str
|
|
7
|
+
identifiers: Dict[str, str]
|
|
8
|
+
groups: Optional[List[str]] = None
|
|
9
|
+
role: Optional[str] = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class JwtPayload(BaseModel):
|
|
13
|
+
user_id: str
|
|
14
|
+
identifiers: Dict[str, str]
|
|
15
|
+
groups: Optional[List[str]] = None
|
|
16
|
+
role: Optional[str] = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class InvitationTarget(BaseModel):
|
|
20
|
+
type: Literal["email", "username", "phoneNumber"]
|
|
21
|
+
value: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Invitation(BaseModel):
|
|
25
|
+
id: str
|
|
26
|
+
target: InvitationTarget
|
|
27
|
+
group_type: Optional[str] = None
|
|
28
|
+
group_id: Optional[str] = None
|
|
29
|
+
status: str
|
|
30
|
+
created_at: str
|
|
31
|
+
updated_at: Optional[str] = None
|
|
32
|
+
expires_at: Optional[str] = None
|
|
33
|
+
metadata: Optional[Dict[str, Union[str, int, bool]]] = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CreateInvitationRequest(BaseModel):
|
|
37
|
+
target: InvitationTarget
|
|
38
|
+
group_type: Optional[str] = None
|
|
39
|
+
group_id: Optional[str] = None
|
|
40
|
+
expires_at: Optional[str] = None
|
|
41
|
+
metadata: Optional[Dict[str, Union[str, int, bool]]] = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AcceptInvitationsRequest(BaseModel):
|
|
45
|
+
invitation_ids: List[str]
|
|
46
|
+
target: InvitationTarget
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ApiResponse(BaseModel):
|
|
50
|
+
data: Optional[Dict] = None
|
|
51
|
+
error: Optional[str] = None
|
|
52
|
+
status_code: int = 200
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class VortexApiError(Exception):
|
|
56
|
+
def __init__(self, message: str, status_code: int = 500):
|
|
57
|
+
self.message = message
|
|
58
|
+
self.status_code = status_code
|
|
59
|
+
super().__init__(message)
|
vortex_sdk/vortex.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import hmac
|
|
3
|
+
import hashlib
|
|
4
|
+
import base64
|
|
5
|
+
from typing import Dict, List, Optional, Union, Literal
|
|
6
|
+
from urllib.parse import urlencode
|
|
7
|
+
import httpx
|
|
8
|
+
from .types import (
|
|
9
|
+
JwtPayload,
|
|
10
|
+
InvitationTarget,
|
|
11
|
+
Invitation,
|
|
12
|
+
CreateInvitationRequest,
|
|
13
|
+
AcceptInvitationsRequest,
|
|
14
|
+
ApiResponse,
|
|
15
|
+
VortexApiError
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Vortex:
|
|
20
|
+
def __init__(self, api_key: str, base_url: str = "https://api.vortexsoftware.com"):
|
|
21
|
+
"""
|
|
22
|
+
Initialize Vortex client
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
api_key: Your Vortex API key
|
|
26
|
+
base_url: Base URL for Vortex API (default: https://api.vortexsoftware.com)
|
|
27
|
+
"""
|
|
28
|
+
self.api_key = api_key
|
|
29
|
+
self.base_url = base_url.rstrip('/')
|
|
30
|
+
self._client = httpx.AsyncClient()
|
|
31
|
+
self._sync_client = httpx.Client()
|
|
32
|
+
|
|
33
|
+
def generate_jwt(self, payload: Union[JwtPayload, Dict]) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Generate a JWT token for the given payload
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
payload: JWT payload containing user_id, identifiers, groups, and role
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
JWT token string
|
|
42
|
+
"""
|
|
43
|
+
if isinstance(payload, dict):
|
|
44
|
+
payload = JwtPayload(**payload)
|
|
45
|
+
|
|
46
|
+
# JWT Header
|
|
47
|
+
header = {
|
|
48
|
+
"alg": "HS256",
|
|
49
|
+
"typ": "JWT"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# JWT Payload
|
|
53
|
+
jwt_payload = {
|
|
54
|
+
"userId": payload.user_id,
|
|
55
|
+
"identifiers": payload.identifiers,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if payload.groups is not None:
|
|
59
|
+
jwt_payload["groups"] = payload.groups
|
|
60
|
+
if payload.role is not None:
|
|
61
|
+
jwt_payload["role"] = payload.role
|
|
62
|
+
|
|
63
|
+
# Encode header and payload
|
|
64
|
+
header_encoded = base64.urlsafe_b64encode(
|
|
65
|
+
json.dumps(header, separators=(',', ':')).encode()
|
|
66
|
+
).decode().rstrip('=')
|
|
67
|
+
|
|
68
|
+
payload_encoded = base64.urlsafe_b64encode(
|
|
69
|
+
json.dumps(jwt_payload, separators=(',', ':')).encode()
|
|
70
|
+
).decode().rstrip('=')
|
|
71
|
+
|
|
72
|
+
# Create signature
|
|
73
|
+
message = f"{header_encoded}.{payload_encoded}"
|
|
74
|
+
signature = hmac.new(
|
|
75
|
+
self.api_key.encode(),
|
|
76
|
+
message.encode(),
|
|
77
|
+
hashlib.sha256
|
|
78
|
+
).digest()
|
|
79
|
+
|
|
80
|
+
signature_encoded = base64.urlsafe_b64encode(signature).decode().rstrip('=')
|
|
81
|
+
|
|
82
|
+
return f"{message}.{signature_encoded}"
|
|
83
|
+
|
|
84
|
+
async def _vortex_api_request(
|
|
85
|
+
self,
|
|
86
|
+
method: str,
|
|
87
|
+
endpoint: str,
|
|
88
|
+
data: Optional[Dict] = None,
|
|
89
|
+
params: Optional[Dict] = None
|
|
90
|
+
) -> Dict:
|
|
91
|
+
"""
|
|
92
|
+
Make an API request to Vortex
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
method: HTTP method (GET, POST, DELETE, etc.)
|
|
96
|
+
endpoint: API endpoint path
|
|
97
|
+
data: Request body data
|
|
98
|
+
params: Query parameters
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
API response data
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
VortexApiError: If the API request fails
|
|
105
|
+
"""
|
|
106
|
+
url = f"{self.base_url}{endpoint}"
|
|
107
|
+
headers = {
|
|
108
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
109
|
+
"Content-Type": "application/json",
|
|
110
|
+
"User-Agent": "vortex-python-sdk/0.0.1"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
response = await self._client.request(
|
|
115
|
+
method=method,
|
|
116
|
+
url=url,
|
|
117
|
+
json=data,
|
|
118
|
+
params=params,
|
|
119
|
+
headers=headers
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if response.status_code >= 400:
|
|
123
|
+
try:
|
|
124
|
+
error_data = response.json()
|
|
125
|
+
error_message = error_data.get('error', f'API request failed with status {response.status_code}')
|
|
126
|
+
except:
|
|
127
|
+
error_message = f'API request failed with status {response.status_code}'
|
|
128
|
+
|
|
129
|
+
raise VortexApiError(error_message, response.status_code)
|
|
130
|
+
|
|
131
|
+
return response.json()
|
|
132
|
+
|
|
133
|
+
except httpx.RequestError as e:
|
|
134
|
+
raise VortexApiError(f"Request failed: {str(e)}")
|
|
135
|
+
|
|
136
|
+
def _vortex_api_request_sync(
|
|
137
|
+
self,
|
|
138
|
+
method: str,
|
|
139
|
+
endpoint: str,
|
|
140
|
+
data: Optional[Dict] = None,
|
|
141
|
+
params: Optional[Dict] = None
|
|
142
|
+
) -> Dict:
|
|
143
|
+
"""
|
|
144
|
+
Make a synchronous API request to Vortex
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
method: HTTP method (GET, POST, DELETE, etc.)
|
|
148
|
+
endpoint: API endpoint path
|
|
149
|
+
data: Request body data
|
|
150
|
+
params: Query parameters
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
API response data
|
|
154
|
+
|
|
155
|
+
Raises:
|
|
156
|
+
VortexApiError: If the API request fails
|
|
157
|
+
"""
|
|
158
|
+
url = f"{self.base_url}{endpoint}"
|
|
159
|
+
headers = {
|
|
160
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
161
|
+
"Content-Type": "application/json",
|
|
162
|
+
"User-Agent": "vortex-python-sdk/0.0.1"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
response = self._sync_client.request(
|
|
167
|
+
method=method,
|
|
168
|
+
url=url,
|
|
169
|
+
json=data,
|
|
170
|
+
params=params,
|
|
171
|
+
headers=headers
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if response.status_code >= 400:
|
|
175
|
+
try:
|
|
176
|
+
error_data = response.json()
|
|
177
|
+
error_message = error_data.get('error', f'API request failed with status {response.status_code}')
|
|
178
|
+
except:
|
|
179
|
+
error_message = f'API request failed with status {response.status_code}'
|
|
180
|
+
|
|
181
|
+
raise VortexApiError(error_message, response.status_code)
|
|
182
|
+
|
|
183
|
+
return response.json()
|
|
184
|
+
|
|
185
|
+
except httpx.RequestError as e:
|
|
186
|
+
raise VortexApiError(f"Request failed: {str(e)}")
|
|
187
|
+
|
|
188
|
+
async def get_invitations_by_target(
|
|
189
|
+
self,
|
|
190
|
+
target_type: Literal["email", "username", "phoneNumber"],
|
|
191
|
+
target_value: str
|
|
192
|
+
) -> List[Invitation]:
|
|
193
|
+
"""
|
|
194
|
+
Get invitations for a specific target
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
target_type: Type of target (email, username, or phoneNumber)
|
|
198
|
+
target_value: Target value
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
List of invitations
|
|
202
|
+
"""
|
|
203
|
+
params = {
|
|
204
|
+
"targetType": target_type,
|
|
205
|
+
"targetValue": target_value
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
response = await self._vortex_api_request("GET", "/invitations/by-target", params=params)
|
|
209
|
+
return [Invitation(**inv) for inv in response.get("invitations", [])]
|
|
210
|
+
|
|
211
|
+
def get_invitations_by_target_sync(
|
|
212
|
+
self,
|
|
213
|
+
target_type: Literal["email", "username", "phoneNumber"],
|
|
214
|
+
target_value: str
|
|
215
|
+
) -> List[Invitation]:
|
|
216
|
+
"""
|
|
217
|
+
Get invitations for a specific target (synchronous)
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
target_type: Type of target (email, username, or phoneNumber)
|
|
221
|
+
target_value: Target value
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
List of invitations
|
|
225
|
+
"""
|
|
226
|
+
params = {
|
|
227
|
+
"targetType": target_type,
|
|
228
|
+
"targetValue": target_value
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
response = self._vortex_api_request_sync("GET", "/invitations/by-target", params=params)
|
|
232
|
+
return [Invitation(**inv) for inv in response.get("invitations", [])]
|
|
233
|
+
|
|
234
|
+
async def get_invitation(self, invitation_id: str) -> Invitation:
|
|
235
|
+
"""
|
|
236
|
+
Get a specific invitation by ID
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
invitation_id: Invitation ID
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Invitation object
|
|
243
|
+
"""
|
|
244
|
+
response = await self._vortex_api_request("GET", f"/invitations/{invitation_id}")
|
|
245
|
+
return Invitation(**response)
|
|
246
|
+
|
|
247
|
+
def get_invitation_sync(self, invitation_id: str) -> Invitation:
|
|
248
|
+
"""
|
|
249
|
+
Get a specific invitation by ID (synchronous)
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
invitation_id: Invitation ID
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Invitation object
|
|
256
|
+
"""
|
|
257
|
+
response = self._vortex_api_request_sync("GET", f"/invitations/{invitation_id}")
|
|
258
|
+
return Invitation(**response)
|
|
259
|
+
|
|
260
|
+
async def accept_invitations(
|
|
261
|
+
self,
|
|
262
|
+
invitation_ids: List[str],
|
|
263
|
+
target: Union[InvitationTarget, Dict[str, str]]
|
|
264
|
+
) -> Dict:
|
|
265
|
+
"""
|
|
266
|
+
Accept multiple invitations
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
invitation_ids: List of invitation IDs to accept
|
|
270
|
+
target: Target information (type and value)
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
API response
|
|
274
|
+
"""
|
|
275
|
+
if isinstance(target, dict):
|
|
276
|
+
target = InvitationTarget(**target)
|
|
277
|
+
|
|
278
|
+
data = {
|
|
279
|
+
"invitationIds": invitation_ids,
|
|
280
|
+
"target": target.model_dump()
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return await self._vortex_api_request("POST", "/invitations/accept", data=data)
|
|
284
|
+
|
|
285
|
+
def accept_invitations_sync(
|
|
286
|
+
self,
|
|
287
|
+
invitation_ids: List[str],
|
|
288
|
+
target: Union[InvitationTarget, Dict[str, str]]
|
|
289
|
+
) -> Dict:
|
|
290
|
+
"""
|
|
291
|
+
Accept multiple invitations (synchronous)
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
invitation_ids: List of invitation IDs to accept
|
|
295
|
+
target: Target information (type and value)
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
API response
|
|
299
|
+
"""
|
|
300
|
+
if isinstance(target, dict):
|
|
301
|
+
target = InvitationTarget(**target)
|
|
302
|
+
|
|
303
|
+
data = {
|
|
304
|
+
"invitationIds": invitation_ids,
|
|
305
|
+
"target": target.model_dump()
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return self._vortex_api_request_sync("POST", "/invitations/accept", data=data)
|
|
309
|
+
|
|
310
|
+
async def revoke_invitation(self, invitation_id: str) -> Dict:
|
|
311
|
+
"""
|
|
312
|
+
Revoke an invitation
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
invitation_id: Invitation ID to revoke
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
API response
|
|
319
|
+
"""
|
|
320
|
+
return await self._vortex_api_request("DELETE", f"/invitations/{invitation_id}")
|
|
321
|
+
|
|
322
|
+
def revoke_invitation_sync(self, invitation_id: str) -> Dict:
|
|
323
|
+
"""
|
|
324
|
+
Revoke an invitation (synchronous)
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
invitation_id: Invitation ID to revoke
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
API response
|
|
331
|
+
"""
|
|
332
|
+
return self._vortex_api_request_sync("DELETE", f"/invitations/{invitation_id}")
|
|
333
|
+
|
|
334
|
+
async def get_invitations_by_group(
|
|
335
|
+
self,
|
|
336
|
+
group_type: str,
|
|
337
|
+
group_id: str
|
|
338
|
+
) -> List[Invitation]:
|
|
339
|
+
"""
|
|
340
|
+
Get invitations for a specific group
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
group_type: Type of group
|
|
344
|
+
group_id: Group ID
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
List of invitations
|
|
348
|
+
"""
|
|
349
|
+
response = await self._vortex_api_request("GET", f"/invitations/by-group/{group_type}/{group_id}")
|
|
350
|
+
return [Invitation(**inv) for inv in response.get("invitations", [])]
|
|
351
|
+
|
|
352
|
+
def get_invitations_by_group_sync(
|
|
353
|
+
self,
|
|
354
|
+
group_type: str,
|
|
355
|
+
group_id: str
|
|
356
|
+
) -> List[Invitation]:
|
|
357
|
+
"""
|
|
358
|
+
Get invitations for a specific group (synchronous)
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
group_type: Type of group
|
|
362
|
+
group_id: Group ID
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
List of invitations
|
|
366
|
+
"""
|
|
367
|
+
response = self._vortex_api_request_sync("GET", f"/invitations/by-group/{group_type}/{group_id}")
|
|
368
|
+
return [Invitation(**inv) for inv in response.get("invitations", [])]
|
|
369
|
+
|
|
370
|
+
async def delete_invitations_by_group(
|
|
371
|
+
self,
|
|
372
|
+
group_type: str,
|
|
373
|
+
group_id: str
|
|
374
|
+
) -> Dict:
|
|
375
|
+
"""
|
|
376
|
+
Delete all invitations for a specific group
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
group_type: Type of group
|
|
380
|
+
group_id: Group ID
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
API response
|
|
384
|
+
"""
|
|
385
|
+
return await self._vortex_api_request("DELETE", f"/invitations/by-group/{group_type}/{group_id}")
|
|
386
|
+
|
|
387
|
+
def delete_invitations_by_group_sync(
|
|
388
|
+
self,
|
|
389
|
+
group_type: str,
|
|
390
|
+
group_id: str
|
|
391
|
+
) -> Dict:
|
|
392
|
+
"""
|
|
393
|
+
Delete all invitations for a specific group (synchronous)
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
group_type: Type of group
|
|
397
|
+
group_id: Group ID
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
API response
|
|
401
|
+
"""
|
|
402
|
+
return self._vortex_api_request_sync("DELETE", f"/invitations/by-group/{group_type}/{group_id}")
|
|
403
|
+
|
|
404
|
+
async def reinvite(self, invitation_id: str) -> Invitation:
|
|
405
|
+
"""
|
|
406
|
+
Reinvite for a specific invitation
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
invitation_id: Invitation ID to reinvite
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
Updated invitation object
|
|
413
|
+
"""
|
|
414
|
+
response = await self._vortex_api_request("POST", f"/invitations/{invitation_id}/reinvite")
|
|
415
|
+
return Invitation(**response)
|
|
416
|
+
|
|
417
|
+
def reinvite_sync(self, invitation_id: str) -> Invitation:
|
|
418
|
+
"""
|
|
419
|
+
Reinvite for a specific invitation (synchronous)
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
invitation_id: Invitation ID to reinvite
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
Updated invitation object
|
|
426
|
+
"""
|
|
427
|
+
response = self._vortex_api_request_sync("POST", f"/invitations/{invitation_id}/reinvite")
|
|
428
|
+
return Invitation(**response)
|
|
429
|
+
|
|
430
|
+
async def close(self):
|
|
431
|
+
"""Close the HTTP client"""
|
|
432
|
+
await self._client.aclose()
|
|
433
|
+
|
|
434
|
+
def close_sync(self):
|
|
435
|
+
"""Close the synchronous HTTP client"""
|
|
436
|
+
self._sync_client.close()
|
|
437
|
+
|
|
438
|
+
async def __aenter__(self):
|
|
439
|
+
"""Async context manager entry"""
|
|
440
|
+
return self
|
|
441
|
+
|
|
442
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
443
|
+
"""Async context manager exit"""
|
|
444
|
+
await self.close()
|
|
445
|
+
|
|
446
|
+
def __enter__(self):
|
|
447
|
+
"""Context manager entry"""
|
|
448
|
+
return self
|
|
449
|
+
|
|
450
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
451
|
+
"""Context manager exit"""
|
|
452
|
+
self.close_sync()
|