Skip to content

Commit 1cce464

Browse files
committed
Approve user invite.
1 parent fb88993 commit 1cce464

File tree

2 files changed

+211
-0
lines changed

2 files changed

+211
-0
lines changed

permit/api/users.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
)
1515
from .context import ApiContextLevel, ApiKeyAccessLevel
1616
from .models import (
17+
ElementsUserInviteApprove,
18+
ElementsUserInviteRead,
1719
PaginatedResultUserRead,
1820
RoleAssignmentCreate,
1921
RoleAssignmentRead,
@@ -58,6 +60,12 @@ def __bulk_operations(self) -> SimpleHttpClient:
5860
f"/v2/facts/{self.config.api_context.project}/{self.config.api_context.environment}/bulk/users"
5961
)
6062

63+
@property
64+
def __user_invites(self) -> SimpleHttpClient:
65+
return self._build_http_client(
66+
f"/v2/facts/{self.config.api_context.project}/{self.config.api_context.environment}/user_invites"
67+
)
68+
6169
@validate_arguments # type: ignore[operator]
6270
async def list(self, page: int = 1, per_page: int = 100) -> PaginatedResultUserRead:
6371
"""
@@ -374,3 +382,27 @@ async def get_assigned_roles(
374382
model=List[RoleAssignmentRead],
375383
params=params,
376384
)
385+
386+
@validate_arguments # type: ignore[operator]
387+
async def approve(self, user_invite_id: str, approve_data: ElementsUserInviteApprove) -> ElementsUserInviteRead:
388+
"""
389+
Approves a user invite.
390+
391+
Args:
392+
user_invite_id: The ID of the user invite to approve.
393+
approve_data: The approval data for the user invite.
394+
395+
Returns:
396+
the approved user invite.
397+
398+
Raises:
399+
PermitApiError: If the API returns an error HTTP status code.
400+
PermitContextError: If the configured ApiContext does not match the required endpoint context.
401+
"""
402+
await self._ensure_access_level(ApiKeyAccessLevel.ENVIRONMENT_LEVEL_API_KEY)
403+
await self._ensure_context(ApiContextLevel.ENVIRONMENT)
404+
return await self.__user_invites.post(
405+
f"/{user_invite_id}/approve",
406+
model=ElementsUserInviteRead,
407+
json=approve_data,
408+
)
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
"""
2+
Simple tests for user invite approve functionality.
3+
These tests focus on the core implementation without complex API mocking.
4+
"""
5+
6+
import uuid
7+
import pytest
8+
from unittest.mock import Mock, AsyncMock, patch
9+
10+
from permit.api.models import ElementsUserInviteApprove, ElementsUserInviteRead, UserInviteStatus
11+
from permit.api.users import UsersApi
12+
13+
14+
class TestUserInviteApproveSimple:
15+
"""Simple test suite for user invite approve functionality."""
16+
17+
def test_approve_method_exists(self):
18+
"""Test that the approve method exists in UsersApi."""
19+
assert hasattr(UsersApi, 'approve')
20+
assert callable(UsersApi.approve)
21+
22+
def test_approve_method_signature(self):
23+
"""Test that the approve method has the correct signature."""
24+
import inspect
25+
26+
method = getattr(UsersApi, 'approve')
27+
signature = inspect.signature(method)
28+
29+
# Check parameter names
30+
params = list(signature.parameters.keys())
31+
assert 'self' in params
32+
assert 'user_invite_id' in params
33+
assert 'approve_data' in params
34+
35+
# Check return type annotation
36+
assert signature.return_annotation.__name__ == 'ElementsUserInviteRead'
37+
38+
def test_approve_data_model_validation(self):
39+
"""Test ElementsUserInviteApprove model validation."""
40+
# Valid data
41+
valid_data = ElementsUserInviteApprove(
42+
email="test@example.com",
43+
key="valid-key-123",
44+
attributes={"role": "admin"}
45+
)
46+
assert valid_data.email == "test@example.com"
47+
assert valid_data.key == "valid-key-123"
48+
assert valid_data.attributes == {"role": "admin"}
49+
50+
def test_approve_data_model_email_validation(self):
51+
"""Test ElementsUserInviteApprove email validation."""
52+
# Test email validation
53+
with pytest.raises(ValueError):
54+
ElementsUserInviteApprove(
55+
email="invalid-email",
56+
key="valid-key-123",
57+
attributes={}
58+
)
59+
60+
def test_approve_data_model_key_validation(self):
61+
"""Test ElementsUserInviteApprove key validation."""
62+
# Test key validation (regex pattern)
63+
with pytest.raises(ValueError):
64+
ElementsUserInviteApprove(
65+
email="test@example.com",
66+
key="invalid key with spaces", # Should fail regex validation
67+
attributes={}
68+
)
69+
70+
def test_approve_data_model_with_complex_attributes(self):
71+
"""Test ElementsUserInviteApprove with complex attributes."""
72+
complex_attributes = {
73+
"department": "Engineering",
74+
"location": "San Francisco",
75+
"role": "Developer",
76+
"level": "Senior",
77+
"permissions": ["read", "write"],
78+
"metadata": {
79+
"hire_date": "2024-01-01",
80+
"manager": "john@example.com"
81+
}
82+
}
83+
84+
approve_data = ElementsUserInviteApprove(
85+
email="test@example.com",
86+
key="test-key-123",
87+
attributes=complex_attributes
88+
)
89+
90+
assert approve_data.attributes == complex_attributes
91+
assert approve_data.attributes["permissions"] == ["read", "write"]
92+
assert approve_data.attributes["metadata"]["hire_date"] == "2024-01-01"
93+
94+
def test_approve_data_model_with_empty_attributes(self):
95+
"""Test ElementsUserInviteApprove with empty attributes."""
96+
approve_data = ElementsUserInviteApprove(
97+
email="test@example.com",
98+
key="test-key-123",
99+
attributes={} # Empty attributes should be allowed
100+
)
101+
102+
assert approve_data.attributes == {}
103+
104+
def test_user_invite_status_enum(self):
105+
"""Test UserInviteStatus enum values."""
106+
assert UserInviteStatus.pending == 'pending'
107+
assert UserInviteStatus.approved == 'approved'
108+
109+
def test_elements_user_invite_read_model(self):
110+
"""Test ElementsUserInviteRead model creation."""
111+
invite_data = ElementsUserInviteRead(
112+
id=uuid.uuid4(),
113+
organization_id=uuid.uuid4(),
114+
project_id=uuid.uuid4(),
115+
environment_id=uuid.uuid4(),
116+
key="test-invite-key-123",
117+
status=UserInviteStatus.approved,
118+
email="test@example.com",
119+
first_name="John",
120+
last_name="Doe",
121+
role_id=uuid.uuid4(),
122+
tenant_id=uuid.uuid4(),
123+
created_at="2024-01-01T00:00:00Z",
124+
updated_at="2024-01-01T01:00:00Z"
125+
)
126+
127+
assert invite_data.status == UserInviteStatus.approved
128+
assert invite_data.email == "test@example.com"
129+
assert invite_data.key == "test-invite-key-123"
130+
131+
132+
def test_approve_parameter_types(self):
133+
"""Test that the approve method parameters have correct types."""
134+
import inspect
135+
from typing import get_type_hints
136+
137+
method = getattr(UsersApi, 'approve')
138+
type_hints = get_type_hints(method)
139+
140+
# Check parameter types
141+
assert type_hints.get('user_invite_id') == str
142+
assert type_hints.get('approve_data').__name__ == 'ElementsUserInviteApprove'
143+
assert type_hints.get('return').__name__ == 'ElementsUserInviteRead'
144+
145+
def test_user_invite_models_exist(self):
146+
"""Test that all required models are available."""
147+
# Test that we can import all necessary models
148+
from permit.api.models import (
149+
ElementsUserInviteApprove,
150+
ElementsUserInviteRead,
151+
UserInviteStatus
152+
)
153+
154+
assert ElementsUserInviteApprove is not None
155+
assert ElementsUserInviteRead is not None
156+
assert UserInviteStatus is not None
157+
158+
def test_approve_data_serialization(self):
159+
"""Test that ElementsUserInviteApprove data can be serialized."""
160+
approve_data = ElementsUserInviteApprove(
161+
email="test@example.com",
162+
key="test-key-123",
163+
attributes={"role": "admin", "department": "Engineering"}
164+
)
165+
166+
# Test dict() method
167+
data_dict = approve_data.dict()
168+
assert data_dict['email'] == "test@example.com"
169+
assert data_dict['key'] == "test-key-123"
170+
assert data_dict['attributes']['role'] == "admin"
171+
assert data_dict['attributes']['department'] == "Engineering"
172+
173+
# Test JSON serialization
174+
import json
175+
json_str = approve_data.json()
176+
parsed_data = json.loads(json_str)
177+
assert parsed_data['email'] == "test@example.com"
178+
assert parsed_data['key'] == "test-key-123"
179+

0 commit comments

Comments
 (0)