vortex-python-sdk 0.0.1__tar.gz → 0.0.2__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.

Potentially problematic release.


This version of vortex-python-sdk might be problematic. Click here for more details.

@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.2] - 2025-01-31
11
+
12
+ ### Fixed
13
+ - **BREAKING FIX**: JWT payload format now matches TypeScript SDK
14
+ - `identifiers` changed from `Dict[str, str]` to `List[Dict]` with `type` and `value` fields
15
+ - `groups` structure now properly includes `type`, `id`/`groupId`, and `name` fields
16
+ - Added `IdentifierInput` type for type-safe identifier creation
17
+ - Updated `GroupInput` to support both `id` (legacy) and `groupId` (preferred) with proper camelCase serialization
18
+ - Updated documentation with correct JWT generation examples
19
+
20
+ ### Migration Guide
21
+ If you're upgrading from 0.0.1, update your JWT generation code:
22
+
23
+ **Before (0.0.1):**
24
+ ```python
25
+ jwt = vortex.generate_jwt({
26
+ "user_id": "user-123",
27
+ "identifiers": {"email": "user@example.com"}, # Dict
28
+ "groups": ["admin"], # List of strings
29
+ })
30
+ ```
31
+
32
+ **After (0.0.2):**
33
+ ```python
34
+ jwt = vortex.generate_jwt({
35
+ "user_id": "user-123",
36
+ "identifiers": [{"type": "email", "value": "user@example.com"}], # List of dicts
37
+ "groups": [{"type": "team", "id": "team-1", "name": "Engineering"}], # List of objects
38
+ })
39
+ ```
40
+
10
41
  ## [0.0.1] - 2024-10-10
11
42
 
12
43
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortex-python-sdk
3
- Version: 0.0.1
3
+ Version: 0.0.2
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
@@ -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": "user123",
71
- "identifiers": {
72
- "email": "user@example.com",
73
- "username": "johndoe"
74
- },
75
- "groups": ["admin", "users"],
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
@@ -29,16 +29,33 @@ vortex = Vortex(api_key="your-api-key", base_url="https://custom-api.example.com
29
29
  ```python
30
30
  # Generate JWT for a user
31
31
  jwt = vortex.generate_jwt({
32
- "user_id": "user123",
33
- "identifiers": {
34
- "email": "user@example.com",
35
- "username": "johndoe"
36
- },
37
- "groups": ["admin", "users"],
32
+ "user_id": "user-123",
33
+ "identifiers": [
34
+ {"type": "email", "value": "user@example.com"}
35
+ ],
36
+ "groups": [
37
+ {"type": "team", "id": "team-1", "name": "Engineering"}
38
+ ],
38
39
  "role": "admin"
39
40
  })
40
41
 
41
42
  print(f"JWT: {jwt}")
43
+
44
+ # Or using type-safe models
45
+ from vortex_sdk import JwtPayload, IdentifierInput, GroupInput
46
+
47
+ jwt = vortex.generate_jwt(
48
+ JwtPayload(
49
+ user_id="user-123",
50
+ identifiers=[
51
+ IdentifierInput(type="email", value="user@example.com")
52
+ ],
53
+ groups=[
54
+ GroupInput(type="team", id="team-1", name="Engineering")
55
+ ],
56
+ role="admin"
57
+ )
58
+ )
42
59
  ```
43
60
 
44
61
  ### Invitation Management
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vortex-python-sdk"
7
- version = "0.0.1"
7
+ version = "0.0.2"
8
8
  description = "Vortex Python SDK for invitation management and JWT generation"
9
9
  authors = [{name = "TeamVortexSoftware", email = "support@vortexsoftware.com"}]
10
10
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortex-python-sdk
3
- Version: 0.0.1
3
+ Version: 0.0.2
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
@@ -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": "user123",
71
- "identifiers": {
72
- "email": "user@example.com",
73
- "username": "johndoe"
74
- },
75
- "groups": ["admin", "users"],
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
@@ -8,6 +8,8 @@ from .vortex import Vortex
8
8
  from .types import (
9
9
  AuthenticatedUser,
10
10
  JwtPayload,
11
+ IdentifierInput,
12
+ GroupInput,
11
13
  InvitationTarget,
12
14
  Invitation,
13
15
  CreateInvitationRequest,
@@ -16,7 +18,7 @@ from .types import (
16
18
  VortexApiError
17
19
  )
18
20
 
19
- __version__ = "0.0.1"
21
+ __version__ = "0.0.2"
20
22
  __author__ = "TeamVortexSoftware"
21
23
  __email__ = "support@vortexsoftware.com"
22
24
 
@@ -24,6 +26,8 @@ __all__ = [
24
26
  "Vortex",
25
27
  "AuthenticatedUser",
26
28
  "JwtPayload",
29
+ "IdentifierInput",
30
+ "GroupInput",
27
31
  "InvitationTarget",
28
32
  "Invitation",
29
33
  "CreateInvitationRequest",
@@ -0,0 +1,102 @@
1
+ from typing import Dict, List, Optional, Union, Literal
2
+ from pydantic import BaseModel, Field
3
+
4
+
5
+ class IdentifierInput(BaseModel):
6
+ """Identifier structure for JWT generation"""
7
+ type: Literal["email", "sms"]
8
+ value: str
9
+
10
+
11
+ class GroupInput(BaseModel):
12
+ """Group structure for JWT generation (input)"""
13
+ type: str
14
+ id: Optional[str] = None # Legacy field (deprecated, use groupId)
15
+ groupId: Optional[str] = Field(None, alias="group_id", serialization_alias="groupId") # Preferred: Customer's group ID
16
+ name: str
17
+
18
+ class Config:
19
+ populate_by_name = True
20
+
21
+
22
+ class InvitationGroup(BaseModel):
23
+ """
24
+ Invitation group from API responses
25
+ This matches the MemberGroups table structure from the API
26
+ """
27
+ id: str # Vortex internal UUID
28
+ account_id: str # Vortex account ID (camelCase in JSON: accountId)
29
+ group_id: str # Customer's group ID (camelCase in JSON: groupId)
30
+ type: str # Group type (e.g., "workspace", "team")
31
+ name: str # Group name
32
+ created_at: str # ISO 8601 timestamp (camelCase in JSON: createdAt)
33
+
34
+ class Config:
35
+ # Allow both snake_case (Python) and camelCase (JSON) field names
36
+ populate_by_name = True
37
+ json_schema_extra = {
38
+ "example": {
39
+ "id": "550e8400-e29b-41d4-a716-446655440000",
40
+ "accountId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
41
+ "groupId": "workspace-123",
42
+ "type": "workspace",
43
+ "name": "My Workspace",
44
+ "createdAt": "2025-01-27T12:00:00.000Z"
45
+ }
46
+ }
47
+
48
+
49
+ class AuthenticatedUser(BaseModel):
50
+ user_id: str
51
+ identifiers: List[IdentifierInput]
52
+ groups: Optional[List[GroupInput]] = None
53
+ role: Optional[str] = None
54
+
55
+
56
+ class JwtPayload(BaseModel):
57
+ user_id: str
58
+ identifiers: List[IdentifierInput]
59
+ groups: Optional[List[GroupInput]] = None
60
+ role: Optional[str] = None
61
+
62
+
63
+ class InvitationTarget(BaseModel):
64
+ type: Literal["email", "username", "phoneNumber"]
65
+ value: str
66
+
67
+
68
+ class Invitation(BaseModel):
69
+ id: str
70
+ target: InvitationTarget
71
+ groups: Optional[List[InvitationGroup]] = None # Full group information
72
+ status: str
73
+ created_at: str
74
+ updated_at: Optional[str] = None
75
+ expires_at: Optional[str] = None
76
+ metadata: Optional[Dict[str, Union[str, int, bool]]] = None
77
+
78
+
79
+ class CreateInvitationRequest(BaseModel):
80
+ target: InvitationTarget
81
+ group_type: Optional[str] = None
82
+ group_id: Optional[str] = None
83
+ expires_at: Optional[str] = None
84
+ metadata: Optional[Dict[str, Union[str, int, bool]]] = None
85
+
86
+
87
+ class AcceptInvitationsRequest(BaseModel):
88
+ invitation_ids: List[str]
89
+ target: InvitationTarget
90
+
91
+
92
+ class ApiResponse(BaseModel):
93
+ data: Optional[Dict] = None
94
+ error: Optional[str] = None
95
+ status_code: int = 200
96
+
97
+
98
+ class VortexApiError(Exception):
99
+ def __init__(self, message: str, status_code: int = 500):
100
+ self.message = message
101
+ self.status_code = status_code
102
+ super().__init__(message)
@@ -49,14 +49,18 @@ class Vortex:
49
49
  "typ": "JWT"
50
50
  }
51
51
 
52
- # JWT Payload
52
+ # JWT Payload - serialize identifiers and groups to dicts
53
53
  jwt_payload = {
54
54
  "userId": payload.user_id,
55
- "identifiers": payload.identifiers,
55
+ "identifiers": [{"type": id.type, "value": id.value} for id in payload.identifiers],
56
56
  }
57
57
 
58
58
  if payload.groups is not None:
59
- jwt_payload["groups"] = payload.groups
59
+ # Serialize groups, using model_dump to handle camelCase conversion
60
+ jwt_payload["groups"] = [
61
+ {k: v for k, v in group.model_dump(by_alias=True, exclude_none=True).items()}
62
+ for group in payload.groups
63
+ ]
60
64
  if payload.role is not None:
61
65
  jwt_payload["role"] = payload.role
62
66
 
@@ -1,59 +0,0 @@
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)