enkryptai-sdk 1.0.24__py3-none-any.whl → 1.0.26__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.
enkryptai_sdk/response.py CHANGED
@@ -1,3 +1,238 @@
1
+ import math
2
+ from typing import Dict, Any, List, Optional, Union
3
+
4
+
5
+ class PaginationInfo:
6
+ """
7
+ A class to handle pagination information and calculations.
8
+ """
9
+
10
+ def __init__(self, page: int = 1, per_page: int = 10, total_count: int = 0):
11
+ """
12
+ Initialize pagination information.
13
+
14
+ Args:
15
+ page (int): Current page number (1-based)
16
+ per_page (int): Number of items per page
17
+ total_count (int): Total number of items
18
+ """
19
+ self.page = max(1, page)
20
+ self.per_page = max(1, min(100, per_page)) # Ensure per_page is between 1 and 100
21
+ self.total_count = max(0, total_count)
22
+
23
+ @property
24
+ def total_pages(self) -> int:
25
+ """Calculate total number of pages."""
26
+ if self.total_count == 0:
27
+ return 0
28
+ return math.ceil(self.total_count / self.per_page)
29
+
30
+ @property
31
+ def has_next(self) -> bool:
32
+ """Check if there's a next page."""
33
+ return self.page < self.total_pages
34
+
35
+ @property
36
+ def has_previous(self) -> bool:
37
+ """Check if there's a previous page."""
38
+ return self.page > 1
39
+
40
+ @property
41
+ def offset(self) -> int:
42
+ """Calculate the offset for database queries."""
43
+ return (self.page - 1) * self.per_page
44
+
45
+ @property
46
+ def limit(self) -> int:
47
+ """Get the limit for database queries."""
48
+ return self.per_page
49
+
50
+ def to_dict(self) -> Dict[str, Any]:
51
+ """Convert pagination info to dictionary."""
52
+ return {
53
+ "page": self.page,
54
+ "per_page": self.per_page,
55
+ "total_count": self.total_count,
56
+ "total_pages": self.total_pages,
57
+ "has_next": self.has_next,
58
+ "has_previous": self.has_previous
59
+ }
60
+
61
+ @classmethod
62
+ def from_query_params(cls, query_params: Dict[str, Any], default_per_page: int = 10) -> "PaginationInfo":
63
+ """
64
+ Create PaginationInfo from query parameters.
65
+
66
+ Args:
67
+ query_params (Dict[str, Any]): Dictionary containing query parameters
68
+ default_per_page (int): Default items per page
69
+
70
+ Returns:
71
+ PaginationInfo: Pagination information object
72
+ """
73
+ try:
74
+ page = int(query_params.get("page", 1))
75
+ per_page = int(query_params.get("per_page", default_per_page))
76
+ except (ValueError, TypeError):
77
+ page = 1
78
+ per_page = default_per_page
79
+
80
+ # Validate pagination parameters
81
+ if page < 1:
82
+ page = 1
83
+ if per_page < 1 or per_page > 100:
84
+ per_page = min(max(1, per_page), 100)
85
+
86
+ return cls(page=page, per_page=per_page)
87
+
88
+ @classmethod
89
+ def validate_params(cls, page: Union[str, int], per_page: Union[str, int]) -> tuple[int, int]:
90
+ """
91
+ Validate pagination parameters and return validated values.
92
+
93
+ Args:
94
+ page: Page number (can be string or int)
95
+ per_page: Items per page (can be string or int)
96
+
97
+ Returns:
98
+ tuple[int, int]: Validated (page, per_page) values
99
+
100
+ Raises:
101
+ ValueError: If parameters are invalid
102
+ """
103
+ try:
104
+ page_num = int(page) if page is not None else 1
105
+ per_page_num = int(per_page) if per_page is not None else 10
106
+ except (ValueError, TypeError):
107
+ raise ValueError("Page and per_page must be valid integers")
108
+
109
+ if page_num < 1:
110
+ raise ValueError("Page must be >= 1")
111
+ if per_page_num < 1 or per_page_num > 100:
112
+ raise ValueError("Per_page must be between 1 and 100")
113
+
114
+ return page_num, per_page_num
115
+
116
+
117
+ class PaginatedResponse(dict):
118
+ """
119
+ A wrapper class for paginated API responses that provides pagination information
120
+ and maintains backward compatibility with dictionary access.
121
+ """
122
+
123
+ def __init__(self, data: List[Any], pagination: PaginationInfo, **kwargs):
124
+ """
125
+ Initialize the PaginatedResponse object.
126
+
127
+ Args:
128
+ data (List[Any]): List of items for the current page
129
+ pagination (PaginationInfo): Pagination information
130
+ **kwargs: Additional response data
131
+ """
132
+ response_data = {
133
+ "data": data,
134
+ "pagination": pagination.to_dict(),
135
+ **kwargs
136
+ }
137
+ super().__init__(response_data)
138
+ self._data = response_data
139
+ self._pagination = pagination
140
+
141
+ def get_data(self) -> List[Any]:
142
+ """Get the data items for the current page."""
143
+ return self._data.get("data", [])
144
+
145
+ def get_pagination(self) -> Dict[str, Any]:
146
+ """Get pagination information."""
147
+ return self._data.get("pagination", {})
148
+
149
+ def get_page(self) -> int:
150
+ """Get current page number."""
151
+ return self._pagination.page
152
+
153
+ def get_per_page(self) -> int:
154
+ """Get items per page."""
155
+ return self._pagination.per_page
156
+
157
+ def get_total_count(self) -> int:
158
+ """Get total number of items."""
159
+ return self._pagination.total_count
160
+
161
+ def get_total_pages(self) -> int:
162
+ """Get total number of pages."""
163
+ return self._pagination.total_pages
164
+
165
+ def has_next_page(self) -> bool:
166
+ """Check if there's a next page."""
167
+ return self._pagination.has_next
168
+
169
+ def has_previous_page(self) -> bool:
170
+ """Check if there's a previous page."""
171
+ return self._pagination.has_previous
172
+
173
+ def get_next_page_url(self, base_url: str, **query_params) -> Optional[str]:
174
+ """
175
+ Generate URL for the next page.
176
+
177
+ Args:
178
+ base_url (str): Base URL for the endpoint
179
+ **query_params: Additional query parameters to include
180
+
181
+ Returns:
182
+ Optional[str]: Next page URL or None if no next page
183
+ """
184
+ if not self.has_next_page():
185
+ return None
186
+
187
+ params = {**query_params, "page": self.get_page() + 1, "per_page": self.get_per_page()}
188
+ query_string = "&".join([f"{k}={v}" for k, v in params.items()])
189
+ return f"{base_url}?{query_string}"
190
+
191
+ def get_previous_page_url(self, base_url: str, **query_params) -> Optional[str]:
192
+ """
193
+ Generate URL for the previous page.
194
+
195
+ Args:
196
+ base_url (str): Base URL for the endpoint
197
+ **query_params: Additional query parameters to include
198
+
199
+ Returns:
200
+ Optional[str]: Previous page URL or None if no previous page
201
+ """
202
+ if not self.has_previous_page():
203
+ return None
204
+
205
+ params = {**query_params, "page": self.get_page() - 1, "per_page": self.get_per_page()}
206
+ query_string = "&".join([f"{k}={v}" for k, v in params.items()])
207
+ return f"{base_url}?{query_string}"
208
+
209
+ def get_page_urls(self, base_url: str, **query_params) -> Dict[str, Optional[str]]:
210
+ """
211
+ Generate URLs for all pagination actions.
212
+
213
+ Args:
214
+ base_url (str): Base URL for the endpoint
215
+ **query_params: Additional query parameters to include
216
+
217
+ Returns:
218
+ Dict[str, Optional[str]]: Dictionary with pagination URLs
219
+ """
220
+ return {
221
+ "first": f"{base_url}?{self._build_query_string(1, **query_params)}",
222
+ "previous": self.get_previous_page_url(base_url, **query_params),
223
+ "current": f"{base_url}?{self._build_query_string(self.get_page(), **query_params)}",
224
+ "next": self.get_next_page_url(base_url, **query_params),
225
+ "last": f"{base_url}?{self._build_query_string(self.get_total_pages(), **query_params)}" if self.get_total_pages() > 0 else None
226
+ }
227
+
228
+ def _build_query_string(self, page: int, **query_params) -> str:
229
+ """Build query string for a specific page."""
230
+ params = {**query_params, "page": page, "per_page": self.get_per_page()}
231
+ return "&".join([f"{k}={v}" for k, v in params.items()])
232
+
233
+ def __str__(self) -> str:
234
+ """String representation of the paginated response."""
235
+ return f"PaginatedResponse(page={self.get_page()}, per_page={self.get_per_page()}, total={self.get_total_count()}, items={len(self.get_data())})"
1
236
 
2
237
 
3
238
  class GuardrailsResponse(dict):
@@ -100,7 +335,25 @@ class GuardrailsResponse(dict):
100
335
 
101
336
  return f"Response Status: {status}\n{violation_str}"
102
337
 
338
+ def get_pagination(self) -> Optional[Dict[str, Any]]:
339
+ """
340
+ Get pagination information if available.
341
+
342
+ Returns:
343
+ Optional[Dict[str, Any]]: Pagination data or None if not available
344
+ """
345
+ return self._data.get("pagination")
103
346
 
347
+ def is_paginated(self) -> bool:
348
+ """
349
+ Check if the response contains pagination information.
350
+
351
+ Returns:
352
+ bool: True if response is paginated, False otherwise
353
+ """
354
+ return "pagination" in self._data
355
+
356
+
104
357
  class PIIResponse(dict):
105
358
  """
106
359
  A wrapper class for Enkrypt AI PII API responses that provides additional functionality
@@ -132,4 +385,22 @@ class PIIResponse(dict):
132
385
  """
133
386
  return self._data.get("key", "")
134
387
 
388
+ def get_pagination(self) -> Optional[Dict[str, Any]]:
389
+ """
390
+ Get pagination information if available.
391
+
392
+ Returns:
393
+ Optional[Dict[str, Any]]: Pagination data or None if not available
394
+ """
395
+ return self._data.get("pagination")
396
+
397
+ def is_paginated(self) -> bool:
398
+ """
399
+ Check if the response contains pagination information.
400
+
401
+ Returns:
402
+ bool: True if response is paginated, False otherwise
403
+ """
404
+ return "pagination" in self._data
405
+
135
406
 
@@ -0,0 +1,29 @@
1
+ # Utils module for Enkrypt AI SDK
2
+
3
+ from .pagination import (
4
+ PaginationInfo,
5
+ PaginatedResponse,
6
+ parse_pagination_params,
7
+ build_pagination_url,
8
+ create_paginated_response,
9
+ validate_pagination_params,
10
+ get_pagination_metadata,
11
+ calculate_page_info,
12
+ create_pagination_links,
13
+ apply_pagination_to_list,
14
+ format_pagination_response
15
+ )
16
+
17
+ __all__ = [
18
+ "PaginationInfo",
19
+ "PaginatedResponse",
20
+ "parse_pagination_params",
21
+ "build_pagination_url",
22
+ "create_paginated_response",
23
+ "validate_pagination_params",
24
+ "get_pagination_metadata",
25
+ "calculate_page_info",
26
+ "create_pagination_links",
27
+ "apply_pagination_to_list",
28
+ "format_pagination_response"
29
+ ]
@@ -0,0 +1,384 @@
1
+ """
2
+ Pagination utilities for Enkrypt AI SDK.
3
+
4
+ This module provides helper functions for handling pagination in API requests and responses.
5
+ """
6
+
7
+ from typing import Dict, Any, List, Optional, Union, Tuple
8
+ from urllib.parse import urlencode, parse_qs, urlparse, urlunparse
9
+ from ..response import PaginationInfo, PaginatedResponse
10
+
11
+
12
+ def parse_pagination_params(
13
+ query_string: str = "",
14
+ default_page: int = 1,
15
+ default_per_page: int = 10
16
+ ) -> PaginationInfo:
17
+ """
18
+ Parse pagination parameters from a query string.
19
+
20
+ Args:
21
+ query_string (str): URL query string (e.g., "page=2&per_page=20")
22
+ default_page (int): Default page number if not specified
23
+ default_per_page (int): Default items per page if not specified
24
+
25
+ Returns:
26
+ PaginationInfo: Parsed pagination information
27
+
28
+ Example:
29
+ >>> parse_pagination_params("page=2&per_page=20")
30
+ PaginationInfo(page=2, per_page=20, total_count=0)
31
+ """
32
+ if not query_string:
33
+ return PaginationInfo(default_page, default_per_page)
34
+
35
+ # Parse query string
36
+ params = parse_qs(query_string)
37
+
38
+ # Extract pagination parameters
39
+ page = params.get("page", [str(default_page)])[0]
40
+ per_page = params.get("per_page", [str(default_per_page)])[0]
41
+
42
+ try:
43
+ page_num = int(page)
44
+ per_page_num = int(per_page)
45
+ except (ValueError, TypeError):
46
+ page_num = default_page
47
+ per_page_num = default_per_page
48
+
49
+ # Validate and create PaginationInfo
50
+ return PaginationInfo(
51
+ page=max(1, page_num),
52
+ per_page=max(1, min(100, per_page_num)),
53
+ total_count=0
54
+ )
55
+
56
+
57
+ def build_pagination_url(
58
+ base_url: str,
59
+ page: int,
60
+ per_page: int,
61
+ **additional_params
62
+ ) -> str:
63
+ """
64
+ Build a URL with pagination parameters.
65
+
66
+ Args:
67
+ base_url (str): Base URL without query parameters
68
+ page (int): Page number
69
+ per_page (int): Items per page
70
+ **additional_params: Additional query parameters
71
+
72
+ Returns:
73
+ str: Complete URL with pagination parameters
74
+
75
+ Example:
76
+ >>> build_pagination_url("https://api.example.com/users", 2, 20, status="active")
77
+ 'https://api.example.com/users?page=2&per_page=20&status=active'
78
+ """
79
+ params = {
80
+ "page": page,
81
+ "per_page": per_page,
82
+ **additional_params
83
+ }
84
+
85
+ # Filter out None values
86
+ params = {k: v for k, v in params.items() if v is not None}
87
+
88
+ query_string = urlencode(params)
89
+ return f"{base_url}?{query_string}" if query_string else base_url
90
+
91
+
92
+ def create_paginated_response(
93
+ data: List[Any],
94
+ total_count: int,
95
+ page: int = 1,
96
+ per_page: int = 10,
97
+ **additional_data
98
+ ) -> PaginatedResponse:
99
+ """
100
+ Create a paginated response object.
101
+
102
+ Args:
103
+ data (List[Any]): List of items for the current page
104
+ total_count (int): Total number of items across all pages
105
+ page (int): Current page number
106
+ per_page (int): Items per page
107
+ **additional_data: Additional data to include in the response
108
+
109
+ Returns:
110
+ PaginatedResponse: Paginated response object
111
+
112
+ Example:
113
+ >>> users = [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]
114
+ >>> response = create_paginated_response(users, 100, 1, 10)
115
+ >>> response.get_page()
116
+ 1
117
+ >>> response.get_total_count()
118
+ 100
119
+ """
120
+ pagination = PaginationInfo(page, per_page, total_count)
121
+ return PaginatedResponse(data, pagination, **additional_data)
122
+
123
+
124
+ def validate_pagination_params(
125
+ page: Union[str, int, None],
126
+ per_page: Union[str, int, None]
127
+ ) -> Tuple[int, int]:
128
+ """
129
+ Validate pagination parameters and return validated values.
130
+
131
+ Args:
132
+ page: Page number (can be string, int, or None)
133
+ per_page: Items per page (can be string, int, or None)
134
+
135
+ Returns:
136
+ Tuple[int, int]: Validated (page, per_page) values
137
+
138
+ Raises:
139
+ ValueError: If parameters are invalid
140
+
141
+ Example:
142
+ >>> validate_pagination_params("2", "20")
143
+ (2, 20)
144
+ >>> validate_pagination_params(None, None)
145
+ (1, 10)
146
+ """
147
+ try:
148
+ page_num = int(page) if page is not None else 1
149
+ per_page_num = int(per_page) if per_page is not None else 10
150
+ except (ValueError, TypeError):
151
+ raise ValueError("Page and per_page must be valid integers")
152
+
153
+ if page_num < 1:
154
+ raise ValueError("Page must be >= 1")
155
+ if per_page_num < 1 or per_page_num > 100:
156
+ raise ValueError("Per_page must be between 1 and 100")
157
+
158
+ return page_num, per_page_num
159
+
160
+
161
+ def get_pagination_metadata(
162
+ total_count: int,
163
+ page: int,
164
+ per_page: int
165
+ ) -> Dict[str, Any]:
166
+ """
167
+ Generate pagination metadata for API responses.
168
+
169
+ Args:
170
+ total_count (int): Total number of items
171
+ page (int): Current page number
172
+ per_page (int): Items per page
173
+
174
+ Returns:
175
+ Dict[str, Any]: Pagination metadata dictionary
176
+
177
+ Example:
178
+ >>> get_pagination_metadata(100, 2, 20)
179
+ {
180
+ 'page': 2,
181
+ 'per_page': 20,
182
+ 'total_count': 100,
183
+ 'total_pages': 5,
184
+ 'has_next': True,
185
+ 'has_previous': True
186
+ }
187
+ """
188
+ pagination = PaginationInfo(page, per_page, total_count)
189
+ return pagination.to_dict()
190
+
191
+
192
+ def calculate_page_info(
193
+ total_count: int,
194
+ page: int,
195
+ per_page: int
196
+ ) -> Dict[str, Any]:
197
+ """
198
+ Calculate detailed pagination information.
199
+
200
+ Args:
201
+ total_count (int): Total number of items
202
+ page (int): Current page number
203
+ per_page (int): Items per page
204
+
205
+ Returns:
206
+ Dict[str, Any]: Detailed pagination information
207
+
208
+ Example:
209
+ >>> calculate_page_info(100, 2, 20)
210
+ {
211
+ 'current_page': 2,
212
+ 'per_page': 20,
213
+ 'total_count': 100,
214
+ 'total_pages': 5,
215
+ 'has_next': True,
216
+ 'has_previous': True,
217
+ 'offset': 20,
218
+ 'limit': 20,
219
+ 'start_item': 21,
220
+ 'end_item': 40
221
+ }
222
+ """
223
+ pagination = PaginationInfo(page, per_page, total_count)
224
+
225
+ start_item = (page - 1) * per_page + 1
226
+ end_item = min(page * per_page, total_count)
227
+
228
+ return {
229
+ 'current_page': pagination.page,
230
+ 'per_page': pagination.per_page,
231
+ 'total_count': pagination.total_count,
232
+ 'total_pages': pagination.total_pages,
233
+ 'has_next': pagination.has_next,
234
+ 'has_previous': pagination.has_previous,
235
+ 'offset': pagination.offset,
236
+ 'limit': pagination.limit,
237
+ 'start_item': start_item if total_count > 0 else 0,
238
+ 'end_item': end_item if total_count > 0 else 0
239
+ }
240
+
241
+
242
+ def create_pagination_links(
243
+ base_url: str,
244
+ current_page: int,
245
+ total_pages: int,
246
+ per_page: int,
247
+ **additional_params
248
+ ) -> Dict[str, Optional[str]]:
249
+ """
250
+ Create pagination links for navigation.
251
+
252
+ Args:
253
+ base_url (str): Base URL for the endpoint
254
+ current_page (int): Current page number
255
+ total_pages (int): Total number of pages
256
+ per_page (int): Items per page
257
+ **additional_params: Additional query parameters
258
+
259
+ Returns:
260
+ Dict[str, Optional[str]]: Dictionary with pagination links
261
+
262
+ Example:
263
+ >>> links = create_pagination_links("https://api.example.com/users", 2, 5, 20)
264
+ >>> links
265
+ {
266
+ 'first': 'https://api.example.com/users?page=1&per_page=20',
267
+ 'previous': 'https://api.example.com/users?page=1&per_page=20',
268
+ 'current': 'https://api.example.com/users?page=2&per_page=20',
269
+ 'next': 'https://api.example.com/users?page=3&per_page=20',
270
+ 'last': 'https://api.example.com/users?page=5&per_page=20'
271
+ }
272
+ """
273
+ links = {}
274
+
275
+ # First page
276
+ links['first'] = build_pagination_url(base_url, 1, per_page, **additional_params)
277
+
278
+ # Previous page
279
+ if current_page > 1:
280
+ links['previous'] = build_pagination_url(base_url, current_page - 1, per_page, **additional_params)
281
+ else:
282
+ links['previous'] = None
283
+
284
+ # Current page
285
+ links['current'] = build_pagination_url(base_url, current_page, per_page, **additional_params)
286
+
287
+ # Next page
288
+ if current_page < total_pages:
289
+ links['next'] = build_pagination_url(base_url, current_page + 1, per_page, **additional_params)
290
+ else:
291
+ links['next'] = None
292
+
293
+ # Last page
294
+ if total_pages > 0:
295
+ links['last'] = build_pagination_url(base_url, total_pages, per_page, **additional_params)
296
+ else:
297
+ links['last'] = None
298
+
299
+ return links
300
+
301
+
302
+ def apply_pagination_to_list(
303
+ data: List[Any],
304
+ page: int,
305
+ per_page: int
306
+ ) -> Tuple[List[Any], int]:
307
+ """
308
+ Apply pagination to a list of data.
309
+
310
+ Args:
311
+ data (List[Any]): Complete list of data
312
+ page (int): Page number (1-based)
313
+ per_page (int): Items per page
314
+
315
+ Returns:
316
+ Tuple[List[Any], int]: (paginated_data, total_count)
317
+
318
+ Example:
319
+ >>> all_users = [{"id": i, "name": f"User{i}"} for i in range(1, 101)]
320
+ >>> page_data, total = apply_pagination_to_list(all_users, 2, 20)
321
+ >>> len(page_data)
322
+ 20
323
+ >>> total
324
+ 100
325
+ """
326
+ total_count = len(data)
327
+ start_index = (page - 1) * per_page
328
+ end_index = start_index + per_page
329
+
330
+ paginated_data = data[start_index:end_index]
331
+ return paginated_data, total_count
332
+
333
+
334
+ def format_pagination_response(
335
+ data: List[Any],
336
+ total_count: int,
337
+ page: int,
338
+ per_page: int,
339
+ include_links: bool = True,
340
+ base_url: str = "",
341
+ **additional_data
342
+ ) -> Dict[str, Any]:
343
+ """
344
+ Format a complete paginated response.
345
+
346
+ Args:
347
+ data (List[Any]): List of items for the current page
348
+ total_count (int): Total number of items
349
+ page (int): Current page number
350
+ per_page (int): Items per page
351
+ include_links (bool): Whether to include pagination links
352
+ base_url (str): Base URL for generating pagination links
353
+ **additional_data: Additional data to include
354
+
355
+ Returns:
356
+ Dict[str, Any]: Formatted paginated response
357
+
358
+ Example:
359
+ >>> response = format_pagination_response(
360
+ ... [{"id": 1, "name": "John"}],
361
+ ... 100, 1, 10,
362
+ ... base_url="https://api.example.com/users"
363
+ ... )
364
+ >>> response.keys()
365
+ dict_keys(['data', 'pagination', 'links'])
366
+ """
367
+ # Create pagination metadata
368
+ pagination = get_pagination_metadata(total_count, page, per_page)
369
+
370
+ # Build response
371
+ response = {
372
+ "data": data,
373
+ "pagination": pagination,
374
+ **additional_data
375
+ }
376
+
377
+ # Add pagination links if requested
378
+ if include_links and base_url:
379
+ links = create_pagination_links(
380
+ base_url, page, pagination['total_pages'], per_page
381
+ )
382
+ response["links"] = links
383
+
384
+ return response
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: enkryptai-sdk
3
- Version: 1.0.24
3
+ Version: 1.0.26
4
4
  Summary: A Python SDK with guardrails and red teaming functionality for API interactions
5
5
  Home-page: https://github.com/enkryptai/enkryptai-sdk
6
6
  Author: Enkrypt AI Team