vortex-python-sdk 0.0.1__py3-none-any.whl → 0.0.5__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 → vortex_python_sdk-0.0.5.dist-info}/METADATA +27 -10
- vortex_python_sdk-0.0.5.dist-info/RECORD +9 -0
- vortex_sdk/__init__.py +13 -9
- vortex_sdk/types.py +66 -13
- vortex_sdk/vortex.py +162 -107
- vortex_python_sdk-0.0.1.dist-info/RECORD +0 -9
- {vortex_python_sdk-0.0.1.dist-info → vortex_python_sdk-0.0.5.dist-info}/WHEEL +0 -0
- {vortex_python_sdk-0.0.1.dist-info → vortex_python_sdk-0.0.5.dist-info}/licenses/LICENSE +0 -0
- {vortex_python_sdk-0.0.1.dist-info → vortex_python_sdk-0.0.5.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vortex-python-sdk
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: Vortex Python SDK for invitation management and JWT generation
|
|
5
5
|
Author-email: TeamVortexSoftware <support@vortexsoftware.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -55,11 +55,11 @@ pip install vortex-python-sdk
|
|
|
55
55
|
```python
|
|
56
56
|
from vortex_sdk import Vortex
|
|
57
57
|
|
|
58
|
-
# Initialize the client
|
|
59
|
-
vortex = Vortex(api_key="your-api-key")
|
|
58
|
+
# Initialize the client with your Vortex API key
|
|
59
|
+
vortex = Vortex(api_key="your-vortex-api-key")
|
|
60
60
|
|
|
61
61
|
# Or with custom base URL
|
|
62
|
-
vortex = Vortex(api_key="your-api-key", base_url="https://custom-api.example.com")
|
|
62
|
+
vortex = Vortex(api_key="your-vortex-api-key", base_url="https://custom-api.example.com")
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
### JWT Generation
|
|
@@ -67,16 +67,33 @@ vortex = Vortex(api_key="your-api-key", base_url="https://custom-api.example.com
|
|
|
67
67
|
```python
|
|
68
68
|
# Generate JWT for a user
|
|
69
69
|
jwt = vortex.generate_jwt({
|
|
70
|
-
"user_id": "
|
|
71
|
-
"identifiers":
|
|
72
|
-
"email": "user@example.com"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
"user_id": "user-123",
|
|
71
|
+
"identifiers": [
|
|
72
|
+
{"type": "email", "value": "user@example.com"}
|
|
73
|
+
],
|
|
74
|
+
"groups": [
|
|
75
|
+
{"type": "team", "id": "team-1", "name": "Engineering"}
|
|
76
|
+
],
|
|
76
77
|
"role": "admin"
|
|
77
78
|
})
|
|
78
79
|
|
|
79
80
|
print(f"JWT: {jwt}")
|
|
81
|
+
|
|
82
|
+
# Or using type-safe models
|
|
83
|
+
from vortex_sdk import JwtPayload, IdentifierInput, GroupInput
|
|
84
|
+
|
|
85
|
+
jwt = vortex.generate_jwt(
|
|
86
|
+
JwtPayload(
|
|
87
|
+
user_id="user-123",
|
|
88
|
+
identifiers=[
|
|
89
|
+
IdentifierInput(type="email", value="user@example.com")
|
|
90
|
+
],
|
|
91
|
+
groups=[
|
|
92
|
+
GroupInput(type="team", id="team-1", name="Engineering")
|
|
93
|
+
],
|
|
94
|
+
role="admin"
|
|
95
|
+
)
|
|
96
|
+
)
|
|
80
97
|
```
|
|
81
98
|
|
|
82
99
|
### Invitation Management
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
vortex_python_sdk-0.0.5.dist-info/licenses/LICENSE,sha256=VndlWxbL4-w3YDf2yE5gJscj4zVXF0qlSq0LDtay3lo,1074
|
|
2
|
+
vortex_sdk/__init__.py,sha256=_39nOXzridOgUJS7MrGv6jt097egE44yVCm6ZkDbFMs,711
|
|
3
|
+
vortex_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
vortex_sdk/types.py,sha256=2YAqEjR5ttZjB1_JfOQWJuPekbc0F-vGlqu8t-jIeho,3344
|
|
5
|
+
vortex_sdk/vortex.py,sha256=qvKWMcd1ISePtV3q8N8-BwBxRdbLQ7sG34nCnLtbpKc,15033
|
|
6
|
+
vortex_python_sdk-0.0.5.dist-info/METADATA,sha256=k_glnwxag_W43RzWI6BJ6wU8NhpjR8GsLcXaK48wutk,6237
|
|
7
|
+
vortex_python_sdk-0.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
vortex_python_sdk-0.0.5.dist-info/top_level.txt,sha256=xFDlEcXIIi_sBhkse0YfMnSdg2IlaYUd0oP2UCDc_Y0,11
|
|
9
|
+
vortex_python_sdk-0.0.5.dist-info/RECORD,,
|
vortex_sdk/__init__.py
CHANGED
|
@@ -4,19 +4,21 @@ Vortex Python SDK
|
|
|
4
4
|
A Python SDK for Vortex invitation management and JWT generation.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from .vortex import Vortex
|
|
8
7
|
from .types import (
|
|
9
|
-
AuthenticatedUser,
|
|
10
|
-
JwtPayload,
|
|
11
|
-
InvitationTarget,
|
|
12
|
-
Invitation,
|
|
13
|
-
CreateInvitationRequest,
|
|
14
8
|
AcceptInvitationsRequest,
|
|
15
9
|
ApiResponse,
|
|
16
|
-
|
|
10
|
+
AuthenticatedUser,
|
|
11
|
+
CreateInvitationRequest,
|
|
12
|
+
GroupInput,
|
|
13
|
+
IdentifierInput,
|
|
14
|
+
Invitation,
|
|
15
|
+
InvitationTarget,
|
|
16
|
+
JwtPayload,
|
|
17
|
+
VortexApiError,
|
|
17
18
|
)
|
|
19
|
+
from .vortex import Vortex
|
|
18
20
|
|
|
19
|
-
__version__ = "0.0.
|
|
21
|
+
__version__ = "0.0.5"
|
|
20
22
|
__author__ = "TeamVortexSoftware"
|
|
21
23
|
__email__ = "support@vortexsoftware.com"
|
|
22
24
|
|
|
@@ -24,10 +26,12 @@ __all__ = [
|
|
|
24
26
|
"Vortex",
|
|
25
27
|
"AuthenticatedUser",
|
|
26
28
|
"JwtPayload",
|
|
29
|
+
"IdentifierInput",
|
|
30
|
+
"GroupInput",
|
|
27
31
|
"InvitationTarget",
|
|
28
32
|
"Invitation",
|
|
29
33
|
"CreateInvitationRequest",
|
|
30
34
|
"AcceptInvitationsRequest",
|
|
31
35
|
"ApiResponse",
|
|
32
36
|
"VortexApiError",
|
|
33
|
-
]
|
|
37
|
+
]
|
vortex_sdk/types.py
CHANGED
|
@@ -1,19 +1,70 @@
|
|
|
1
|
-
from typing import Dict, List, Optional, Union
|
|
2
|
-
|
|
1
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IdentifierInput(BaseModel):
|
|
7
|
+
"""Identifier structure for JWT generation"""
|
|
8
|
+
|
|
9
|
+
type: Literal["email", "sms"]
|
|
10
|
+
value: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GroupInput(BaseModel):
|
|
14
|
+
"""Group structure for JWT generation (input)"""
|
|
15
|
+
|
|
16
|
+
type: str
|
|
17
|
+
id: Optional[str] = None # Legacy field (deprecated, use groupId)
|
|
18
|
+
groupId: Optional[str] = Field(
|
|
19
|
+
None, alias="group_id", serialization_alias="groupId"
|
|
20
|
+
) # Preferred: Customer's group ID
|
|
21
|
+
name: str
|
|
22
|
+
|
|
23
|
+
class Config:
|
|
24
|
+
populate_by_name = True
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class InvitationGroup(BaseModel):
|
|
28
|
+
"""
|
|
29
|
+
Invitation group from API responses
|
|
30
|
+
This matches the MemberGroups table structure from the API
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
id: str # Vortex internal UUID
|
|
34
|
+
account_id: str = Field(alias="accountId") # Vortex account ID
|
|
35
|
+
group_id: str = Field(alias="groupId") # Customer's group ID
|
|
36
|
+
type: str # Group type (e.g., "workspace", "team")
|
|
37
|
+
name: str # Group name
|
|
38
|
+
created_at: str = Field(alias="createdAt") # ISO 8601 timestamp
|
|
39
|
+
|
|
40
|
+
class Config:
|
|
41
|
+
# Allow both snake_case (Python) and camelCase (JSON) field names
|
|
42
|
+
populate_by_name = True
|
|
43
|
+
json_schema_extra = {
|
|
44
|
+
"example": {
|
|
45
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
46
|
+
"accountId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
47
|
+
"groupId": "workspace-123",
|
|
48
|
+
"type": "workspace",
|
|
49
|
+
"name": "My Workspace",
|
|
50
|
+
"createdAt": "2025-01-27T12:00:00.000Z",
|
|
51
|
+
}
|
|
52
|
+
}
|
|
3
53
|
|
|
4
54
|
|
|
5
55
|
class AuthenticatedUser(BaseModel):
|
|
6
56
|
user_id: str
|
|
7
|
-
identifiers:
|
|
8
|
-
groups: Optional[List[
|
|
57
|
+
identifiers: List[IdentifierInput]
|
|
58
|
+
groups: Optional[List[GroupInput]] = None
|
|
9
59
|
role: Optional[str] = None
|
|
10
60
|
|
|
11
61
|
|
|
12
62
|
class JwtPayload(BaseModel):
|
|
13
63
|
user_id: str
|
|
14
|
-
identifiers:
|
|
15
|
-
groups: Optional[List[
|
|
64
|
+
identifiers: List[IdentifierInput]
|
|
65
|
+
groups: Optional[List[GroupInput]] = None
|
|
16
66
|
role: Optional[str] = None
|
|
67
|
+
attributes: Optional[Dict[str, Any]] = None
|
|
17
68
|
|
|
18
69
|
|
|
19
70
|
class InvitationTarget(BaseModel):
|
|
@@ -23,15 +74,17 @@ class InvitationTarget(BaseModel):
|
|
|
23
74
|
|
|
24
75
|
class Invitation(BaseModel):
|
|
25
76
|
id: str
|
|
26
|
-
target: InvitationTarget
|
|
27
|
-
|
|
28
|
-
group_id: Optional[str] = None
|
|
77
|
+
target: Union[InvitationTarget, List[InvitationTarget]] # API returns list or single
|
|
78
|
+
groups: Optional[List[Optional[InvitationGroup]]] = None # Full group information, can contain None
|
|
29
79
|
status: str
|
|
30
|
-
created_at: str
|
|
31
|
-
updated_at: Optional[str] = None
|
|
32
|
-
expires_at: Optional[str] = None
|
|
80
|
+
created_at: Optional[str] = Field(None, alias="createdAt") # API uses camelCase
|
|
81
|
+
updated_at: Optional[str] = Field(None, alias="updatedAt")
|
|
82
|
+
expires_at: Optional[str] = Field(None, alias="expiresAt")
|
|
33
83
|
metadata: Optional[Dict[str, Union[str, int, bool]]] = None
|
|
34
84
|
|
|
85
|
+
class Config:
|
|
86
|
+
populate_by_name = True
|
|
87
|
+
|
|
35
88
|
|
|
36
89
|
class CreateInvitationRequest(BaseModel):
|
|
37
90
|
target: InvitationTarget
|
|
@@ -56,4 +109,4 @@ class VortexApiError(Exception):
|
|
|
56
109
|
def __init__(self, message: str, status_code: int = 500):
|
|
57
110
|
self.message = message
|
|
58
111
|
self.status_code = status_code
|
|
59
|
-
super().__init__(message)
|
|
112
|
+
super().__init__(message)
|
vortex_sdk/vortex.py
CHANGED
|
@@ -1,92 +1,155 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import hmac
|
|
3
|
-
import hashlib
|
|
4
1
|
import base64
|
|
5
|
-
|
|
2
|
+
import hashlib
|
|
3
|
+
import hmac
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
import uuid
|
|
7
|
+
from typing import Dict, List, Literal, Optional, Union
|
|
6
8
|
from urllib.parse import urlencode
|
|
9
|
+
|
|
7
10
|
import httpx
|
|
11
|
+
|
|
8
12
|
from .types import (
|
|
9
|
-
JwtPayload,
|
|
10
|
-
InvitationTarget,
|
|
11
|
-
Invitation,
|
|
12
|
-
CreateInvitationRequest,
|
|
13
13
|
AcceptInvitationsRequest,
|
|
14
14
|
ApiResponse,
|
|
15
|
-
|
|
15
|
+
CreateInvitationRequest,
|
|
16
|
+
Invitation,
|
|
17
|
+
InvitationTarget,
|
|
18
|
+
JwtPayload,
|
|
19
|
+
VortexApiError,
|
|
16
20
|
)
|
|
17
21
|
|
|
18
22
|
|
|
23
|
+
def _get_version():
|
|
24
|
+
"""Lazy import of version to avoid circular import"""
|
|
25
|
+
from . import __version__
|
|
26
|
+
return __version__
|
|
27
|
+
|
|
28
|
+
|
|
19
29
|
class Vortex:
|
|
20
|
-
def __init__(
|
|
30
|
+
def __init__(
|
|
31
|
+
self, api_key: str, base_url: str = "https://api.vortexsoftware.com/api/v1"
|
|
32
|
+
):
|
|
21
33
|
"""
|
|
22
34
|
Initialize Vortex client
|
|
23
35
|
|
|
24
36
|
Args:
|
|
25
37
|
api_key: Your Vortex API key
|
|
26
|
-
base_url: Base URL for Vortex API (default: https://api.vortexsoftware.com)
|
|
38
|
+
base_url: Base URL for Vortex API (default: https://api.vortexsoftware.com/api/v1)
|
|
27
39
|
"""
|
|
28
40
|
self.api_key = api_key
|
|
29
|
-
self.base_url = base_url.rstrip(
|
|
41
|
+
self.base_url = base_url.rstrip("/")
|
|
30
42
|
self._client = httpx.AsyncClient()
|
|
31
43
|
self._sync_client = httpx.Client()
|
|
32
44
|
|
|
33
45
|
def generate_jwt(self, payload: Union[JwtPayload, Dict]) -> str:
|
|
34
46
|
"""
|
|
35
|
-
Generate a JWT token for the given payload
|
|
47
|
+
Generate a JWT token for the given payload matching Node.js SDK implementation
|
|
36
48
|
|
|
37
49
|
Args:
|
|
38
50
|
payload: JWT payload containing user_id, identifiers, groups, and role
|
|
39
51
|
|
|
40
52
|
Returns:
|
|
41
53
|
JWT token string
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
ValueError: If API key format is invalid
|
|
42
57
|
"""
|
|
43
58
|
if isinstance(payload, dict):
|
|
44
59
|
payload = JwtPayload(**payload)
|
|
45
60
|
|
|
46
|
-
#
|
|
61
|
+
# Parse API key (format: VRTX.base64url(uuid).key)
|
|
62
|
+
parts = self.api_key.split(".")
|
|
63
|
+
if len(parts) != 3:
|
|
64
|
+
raise ValueError("Invalid API key format. Expected: VRTX.{encodedId}.{key}")
|
|
65
|
+
|
|
66
|
+
prefix, encoded_id, key = parts
|
|
67
|
+
|
|
68
|
+
if prefix != "VRTX":
|
|
69
|
+
raise ValueError("Invalid API key prefix. Expected: VRTX")
|
|
70
|
+
|
|
71
|
+
# Decode UUID from base64url
|
|
72
|
+
# Add padding if needed
|
|
73
|
+
padding = 4 - len(encoded_id) % 4
|
|
74
|
+
if padding != 4:
|
|
75
|
+
encoded_id_padded = encoded_id + ("=" * padding)
|
|
76
|
+
else:
|
|
77
|
+
encoded_id_padded = encoded_id
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
uuid_bytes = base64.urlsafe_b64decode(encoded_id_padded)
|
|
81
|
+
kid = str(uuid.UUID(bytes=uuid_bytes))
|
|
82
|
+
except Exception as e:
|
|
83
|
+
raise ValueError(f"Invalid UUID in API key: {e}")
|
|
84
|
+
|
|
85
|
+
# Generate timestamps
|
|
86
|
+
iat = int(time.time())
|
|
87
|
+
expires = iat + 3600
|
|
88
|
+
|
|
89
|
+
# Step 1: Derive signing key from API key + UUID
|
|
90
|
+
signing_key = hmac.new(key.encode(), kid.encode(), hashlib.sha256).digest()
|
|
91
|
+
|
|
92
|
+
# Step 2: Build header + payload
|
|
47
93
|
header = {
|
|
94
|
+
"iat": iat,
|
|
48
95
|
"alg": "HS256",
|
|
49
|
-
"typ": "JWT"
|
|
96
|
+
"typ": "JWT",
|
|
97
|
+
"kid": kid,
|
|
50
98
|
}
|
|
51
99
|
|
|
52
|
-
#
|
|
100
|
+
# Serialize identifiers
|
|
101
|
+
identifiers_list = [
|
|
102
|
+
{"type": id.type, "value": id.value} for id in payload.identifiers
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
# Serialize groups
|
|
106
|
+
groups_list = None
|
|
107
|
+
if payload.groups is not None:
|
|
108
|
+
groups_list = [
|
|
109
|
+
{
|
|
110
|
+
k: v
|
|
111
|
+
for k, v in group.model_dump(
|
|
112
|
+
by_alias=True, exclude_none=True
|
|
113
|
+
).items()
|
|
114
|
+
}
|
|
115
|
+
for group in payload.groups
|
|
116
|
+
]
|
|
117
|
+
|
|
53
118
|
jwt_payload = {
|
|
54
119
|
"userId": payload.user_id,
|
|
55
|
-
"
|
|
120
|
+
"groups": groups_list,
|
|
121
|
+
"role": payload.role,
|
|
122
|
+
"expires": expires,
|
|
123
|
+
"identifiers": identifiers_list,
|
|
56
124
|
}
|
|
57
125
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
jwt_payload["role"] = payload.role
|
|
126
|
+
# Add attributes if provided
|
|
127
|
+
if hasattr(payload, "attributes") and payload.attributes:
|
|
128
|
+
jwt_payload["attributes"] = payload.attributes
|
|
62
129
|
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
).decode().rstrip('=')
|
|
130
|
+
# Step 3: Base64URL encode (without padding)
|
|
131
|
+
header_json = json.dumps(header, separators=(",", ":"))
|
|
132
|
+
payload_json = json.dumps(jwt_payload, separators=(",", ":"))
|
|
67
133
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
134
|
+
header_b64 = base64.urlsafe_b64encode(header_json.encode()).decode().rstrip("=")
|
|
135
|
+
payload_b64 = (
|
|
136
|
+
base64.urlsafe_b64encode(payload_json.encode()).decode().rstrip("=")
|
|
137
|
+
)
|
|
71
138
|
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
signature = hmac.new(
|
|
75
|
-
self.api_key.encode(),
|
|
76
|
-
message.encode(),
|
|
77
|
-
hashlib.sha256
|
|
78
|
-
).digest()
|
|
139
|
+
# Step 4: Sign
|
|
140
|
+
to_sign = f"{header_b64}.{payload_b64}"
|
|
141
|
+
signature = hmac.new(signing_key, to_sign.encode(), hashlib.sha256).digest()
|
|
79
142
|
|
|
80
|
-
|
|
143
|
+
signature_b64 = base64.urlsafe_b64encode(signature).decode().rstrip("=")
|
|
81
144
|
|
|
82
|
-
return f"{
|
|
145
|
+
return f"{to_sign}.{signature_b64}"
|
|
83
146
|
|
|
84
147
|
async def _vortex_api_request(
|
|
85
148
|
self,
|
|
86
149
|
method: str,
|
|
87
150
|
endpoint: str,
|
|
88
151
|
data: Optional[Dict] = None,
|
|
89
|
-
params: Optional[Dict] = None
|
|
152
|
+
params: Optional[Dict] = None,
|
|
90
153
|
) -> Dict:
|
|
91
154
|
"""
|
|
92
155
|
Make an API request to Vortex
|
|
@@ -105,26 +168,27 @@ class Vortex:
|
|
|
105
168
|
"""
|
|
106
169
|
url = f"{self.base_url}{endpoint}"
|
|
107
170
|
headers = {
|
|
108
|
-
"
|
|
171
|
+
"x-api-key": f"{self.api_key}",
|
|
109
172
|
"Content-Type": "application/json",
|
|
110
|
-
"User-Agent": "vortex-python-sdk/
|
|
173
|
+
"User-Agent": f"vortex-python-sdk/{_get_version()}",
|
|
111
174
|
}
|
|
112
175
|
|
|
113
176
|
try:
|
|
114
177
|
response = await self._client.request(
|
|
115
|
-
method=method,
|
|
116
|
-
url=url,
|
|
117
|
-
json=data,
|
|
118
|
-
params=params,
|
|
119
|
-
headers=headers
|
|
178
|
+
method=method, url=url, json=data, params=params, headers=headers
|
|
120
179
|
)
|
|
121
180
|
|
|
122
181
|
if response.status_code >= 400:
|
|
123
182
|
try:
|
|
124
183
|
error_data = response.json()
|
|
125
|
-
error_message = error_data.get(
|
|
184
|
+
error_message = error_data.get(
|
|
185
|
+
"error",
|
|
186
|
+
f"API request failed with status {response.status_code}",
|
|
187
|
+
)
|
|
126
188
|
except:
|
|
127
|
-
error_message =
|
|
189
|
+
error_message = (
|
|
190
|
+
f"API request failed with status {response.status_code}"
|
|
191
|
+
)
|
|
128
192
|
|
|
129
193
|
raise VortexApiError(error_message, response.status_code)
|
|
130
194
|
|
|
@@ -138,7 +202,7 @@ class Vortex:
|
|
|
138
202
|
method: str,
|
|
139
203
|
endpoint: str,
|
|
140
204
|
data: Optional[Dict] = None,
|
|
141
|
-
params: Optional[Dict] = None
|
|
205
|
+
params: Optional[Dict] = None,
|
|
142
206
|
) -> Dict:
|
|
143
207
|
"""
|
|
144
208
|
Make a synchronous API request to Vortex
|
|
@@ -157,26 +221,27 @@ class Vortex:
|
|
|
157
221
|
"""
|
|
158
222
|
url = f"{self.base_url}{endpoint}"
|
|
159
223
|
headers = {
|
|
160
|
-
"
|
|
224
|
+
"x-api-key": f"{self.api_key}",
|
|
161
225
|
"Content-Type": "application/json",
|
|
162
|
-
"User-Agent": "vortex-python-sdk/
|
|
226
|
+
"User-Agent": f"vortex-python-sdk/{_get_version()}",
|
|
163
227
|
}
|
|
164
228
|
|
|
165
229
|
try:
|
|
166
230
|
response = self._sync_client.request(
|
|
167
|
-
method=method,
|
|
168
|
-
url=url,
|
|
169
|
-
json=data,
|
|
170
|
-
params=params,
|
|
171
|
-
headers=headers
|
|
231
|
+
method=method, url=url, json=data, params=params, headers=headers
|
|
172
232
|
)
|
|
173
233
|
|
|
174
234
|
if response.status_code >= 400:
|
|
175
235
|
try:
|
|
176
236
|
error_data = response.json()
|
|
177
|
-
error_message = error_data.get(
|
|
237
|
+
error_message = error_data.get(
|
|
238
|
+
"error",
|
|
239
|
+
f"API request failed with status {response.status_code}",
|
|
240
|
+
)
|
|
178
241
|
except:
|
|
179
|
-
error_message =
|
|
242
|
+
error_message = (
|
|
243
|
+
f"API request failed with status {response.status_code}"
|
|
244
|
+
)
|
|
180
245
|
|
|
181
246
|
raise VortexApiError(error_message, response.status_code)
|
|
182
247
|
|
|
@@ -188,7 +253,7 @@ class Vortex:
|
|
|
188
253
|
async def get_invitations_by_target(
|
|
189
254
|
self,
|
|
190
255
|
target_type: Literal["email", "username", "phoneNumber"],
|
|
191
|
-
target_value: str
|
|
256
|
+
target_value: str,
|
|
192
257
|
) -> List[Invitation]:
|
|
193
258
|
"""
|
|
194
259
|
Get invitations for a specific target
|
|
@@ -200,18 +265,17 @@ class Vortex:
|
|
|
200
265
|
Returns:
|
|
201
266
|
List of invitations
|
|
202
267
|
"""
|
|
203
|
-
params = {
|
|
204
|
-
"targetType": target_type,
|
|
205
|
-
"targetValue": target_value
|
|
206
|
-
}
|
|
268
|
+
params = {"targetType": target_type, "targetValue": target_value}
|
|
207
269
|
|
|
208
|
-
response = await self._vortex_api_request(
|
|
270
|
+
response = await self._vortex_api_request(
|
|
271
|
+
"GET", "/invitations/by-target", params=params
|
|
272
|
+
)
|
|
209
273
|
return [Invitation(**inv) for inv in response.get("invitations", [])]
|
|
210
274
|
|
|
211
275
|
def get_invitations_by_target_sync(
|
|
212
276
|
self,
|
|
213
277
|
target_type: Literal["email", "username", "phoneNumber"],
|
|
214
|
-
target_value: str
|
|
278
|
+
target_value: str,
|
|
215
279
|
) -> List[Invitation]:
|
|
216
280
|
"""
|
|
217
281
|
Get invitations for a specific target (synchronous)
|
|
@@ -223,12 +287,11 @@ class Vortex:
|
|
|
223
287
|
Returns:
|
|
224
288
|
List of invitations
|
|
225
289
|
"""
|
|
226
|
-
params = {
|
|
227
|
-
"targetType": target_type,
|
|
228
|
-
"targetValue": target_value
|
|
229
|
-
}
|
|
290
|
+
params = {"targetType": target_type, "targetValue": target_value}
|
|
230
291
|
|
|
231
|
-
response = self._vortex_api_request_sync(
|
|
292
|
+
response = self._vortex_api_request_sync(
|
|
293
|
+
"GET", "/invitations/by-target", params=params
|
|
294
|
+
)
|
|
232
295
|
return [Invitation(**inv) for inv in response.get("invitations", [])]
|
|
233
296
|
|
|
234
297
|
async def get_invitation(self, invitation_id: str) -> Invitation:
|
|
@@ -241,7 +304,9 @@ class Vortex:
|
|
|
241
304
|
Returns:
|
|
242
305
|
Invitation object
|
|
243
306
|
"""
|
|
244
|
-
response = await self._vortex_api_request(
|
|
307
|
+
response = await self._vortex_api_request(
|
|
308
|
+
"GET", f"/invitations/{invitation_id}"
|
|
309
|
+
)
|
|
245
310
|
return Invitation(**response)
|
|
246
311
|
|
|
247
312
|
def get_invitation_sync(self, invitation_id: str) -> Invitation:
|
|
@@ -258,9 +323,7 @@ class Vortex:
|
|
|
258
323
|
return Invitation(**response)
|
|
259
324
|
|
|
260
325
|
async def accept_invitations(
|
|
261
|
-
self,
|
|
262
|
-
invitation_ids: List[str],
|
|
263
|
-
target: Union[InvitationTarget, Dict[str, str]]
|
|
326
|
+
self, invitation_ids: List[str], target: Union[InvitationTarget, Dict[str, str]]
|
|
264
327
|
) -> Dict:
|
|
265
328
|
"""
|
|
266
329
|
Accept multiple invitations
|
|
@@ -275,17 +338,12 @@ class Vortex:
|
|
|
275
338
|
if isinstance(target, dict):
|
|
276
339
|
target = InvitationTarget(**target)
|
|
277
340
|
|
|
278
|
-
data = {
|
|
279
|
-
"invitationIds": invitation_ids,
|
|
280
|
-
"target": target.model_dump()
|
|
281
|
-
}
|
|
341
|
+
data = {"invitationIds": invitation_ids, "target": target.model_dump()}
|
|
282
342
|
|
|
283
343
|
return await self._vortex_api_request("POST", "/invitations/accept", data=data)
|
|
284
344
|
|
|
285
345
|
def accept_invitations_sync(
|
|
286
|
-
self,
|
|
287
|
-
invitation_ids: List[str],
|
|
288
|
-
target: Union[InvitationTarget, Dict[str, str]]
|
|
346
|
+
self, invitation_ids: List[str], target: Union[InvitationTarget, Dict[str, str]]
|
|
289
347
|
) -> Dict:
|
|
290
348
|
"""
|
|
291
349
|
Accept multiple invitations (synchronous)
|
|
@@ -300,10 +358,7 @@ class Vortex:
|
|
|
300
358
|
if isinstance(target, dict):
|
|
301
359
|
target = InvitationTarget(**target)
|
|
302
360
|
|
|
303
|
-
data = {
|
|
304
|
-
"invitationIds": invitation_ids,
|
|
305
|
-
"target": target.model_dump()
|
|
306
|
-
}
|
|
361
|
+
data = {"invitationIds": invitation_ids, "target": target.model_dump()}
|
|
307
362
|
|
|
308
363
|
return self._vortex_api_request_sync("POST", "/invitations/accept", data=data)
|
|
309
364
|
|
|
@@ -332,9 +387,7 @@ class Vortex:
|
|
|
332
387
|
return self._vortex_api_request_sync("DELETE", f"/invitations/{invitation_id}")
|
|
333
388
|
|
|
334
389
|
async def get_invitations_by_group(
|
|
335
|
-
self,
|
|
336
|
-
group_type: str,
|
|
337
|
-
group_id: str
|
|
390
|
+
self, group_type: str, group_id: str
|
|
338
391
|
) -> List[Invitation]:
|
|
339
392
|
"""
|
|
340
393
|
Get invitations for a specific group
|
|
@@ -346,13 +399,13 @@ class Vortex:
|
|
|
346
399
|
Returns:
|
|
347
400
|
List of invitations
|
|
348
401
|
"""
|
|
349
|
-
response = await self._vortex_api_request(
|
|
402
|
+
response = await self._vortex_api_request(
|
|
403
|
+
"GET", f"/invitations/by-group/{group_type}/{group_id}"
|
|
404
|
+
)
|
|
350
405
|
return [Invitation(**inv) for inv in response.get("invitations", [])]
|
|
351
406
|
|
|
352
407
|
def get_invitations_by_group_sync(
|
|
353
|
-
self,
|
|
354
|
-
group_type: str,
|
|
355
|
-
group_id: str
|
|
408
|
+
self, group_type: str, group_id: str
|
|
356
409
|
) -> List[Invitation]:
|
|
357
410
|
"""
|
|
358
411
|
Get invitations for a specific group (synchronous)
|
|
@@ -364,14 +417,12 @@ class Vortex:
|
|
|
364
417
|
Returns:
|
|
365
418
|
List of invitations
|
|
366
419
|
"""
|
|
367
|
-
response = self._vortex_api_request_sync(
|
|
420
|
+
response = self._vortex_api_request_sync(
|
|
421
|
+
"GET", f"/invitations/by-group/{group_type}/{group_id}"
|
|
422
|
+
)
|
|
368
423
|
return [Invitation(**inv) for inv in response.get("invitations", [])]
|
|
369
424
|
|
|
370
|
-
async def delete_invitations_by_group(
|
|
371
|
-
self,
|
|
372
|
-
group_type: str,
|
|
373
|
-
group_id: str
|
|
374
|
-
) -> Dict:
|
|
425
|
+
async def delete_invitations_by_group(self, group_type: str, group_id: str) -> Dict:
|
|
375
426
|
"""
|
|
376
427
|
Delete all invitations for a specific group
|
|
377
428
|
|
|
@@ -382,13 +433,11 @@ class Vortex:
|
|
|
382
433
|
Returns:
|
|
383
434
|
API response
|
|
384
435
|
"""
|
|
385
|
-
return await self._vortex_api_request(
|
|
436
|
+
return await self._vortex_api_request(
|
|
437
|
+
"DELETE", f"/invitations/by-group/{group_type}/{group_id}"
|
|
438
|
+
)
|
|
386
439
|
|
|
387
|
-
def delete_invitations_by_group_sync(
|
|
388
|
-
self,
|
|
389
|
-
group_type: str,
|
|
390
|
-
group_id: str
|
|
391
|
-
) -> Dict:
|
|
440
|
+
def delete_invitations_by_group_sync(self, group_type: str, group_id: str) -> Dict:
|
|
392
441
|
"""
|
|
393
442
|
Delete all invitations for a specific group (synchronous)
|
|
394
443
|
|
|
@@ -399,7 +448,9 @@ class Vortex:
|
|
|
399
448
|
Returns:
|
|
400
449
|
API response
|
|
401
450
|
"""
|
|
402
|
-
return self._vortex_api_request_sync(
|
|
451
|
+
return self._vortex_api_request_sync(
|
|
452
|
+
"DELETE", f"/invitations/by-group/{group_type}/{group_id}"
|
|
453
|
+
)
|
|
403
454
|
|
|
404
455
|
async def reinvite(self, invitation_id: str) -> Invitation:
|
|
405
456
|
"""
|
|
@@ -411,7 +462,9 @@ class Vortex:
|
|
|
411
462
|
Returns:
|
|
412
463
|
Updated invitation object
|
|
413
464
|
"""
|
|
414
|
-
response = await self._vortex_api_request(
|
|
465
|
+
response = await self._vortex_api_request(
|
|
466
|
+
"POST", f"/invitations/{invitation_id}/reinvite"
|
|
467
|
+
)
|
|
415
468
|
return Invitation(**response)
|
|
416
469
|
|
|
417
470
|
def reinvite_sync(self, invitation_id: str) -> Invitation:
|
|
@@ -424,7 +477,9 @@ class Vortex:
|
|
|
424
477
|
Returns:
|
|
425
478
|
Updated invitation object
|
|
426
479
|
"""
|
|
427
|
-
response = self._vortex_api_request_sync(
|
|
480
|
+
response = self._vortex_api_request_sync(
|
|
481
|
+
"POST", f"/invitations/{invitation_id}/reinvite"
|
|
482
|
+
)
|
|
428
483
|
return Invitation(**response)
|
|
429
484
|
|
|
430
485
|
async def close(self):
|
|
@@ -449,4 +504,4 @@ class Vortex:
|
|
|
449
504
|
|
|
450
505
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
451
506
|
"""Context manager exit"""
|
|
452
|
-
self.close_sync()
|
|
507
|
+
self.close_sync()
|
|
@@ -1,9 +0,0 @@
|
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|