cohere-compass-sdk 1.3.2__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.
@@ -0,0 +1,48 @@
1
+ # Python imports
2
+ from enum import Enum
3
+ from importlib import metadata
4
+ from typing import Optional
5
+
6
+ # 3rd party imports
7
+ from pydantic import BaseModel
8
+
9
+ # Local imports
10
+ from cohere_compass.models import (
11
+ MetadataConfig,
12
+ ParserConfig,
13
+ ValidatedModel,
14
+ )
15
+
16
+ __version__ = metadata.version("cohere-compass-sdk")
17
+
18
+
19
+ class ProcessFileParameters(ValidatedModel):
20
+ """Model for use with the process_file parser API."""
21
+
22
+ parser_config: ParserConfig
23
+ metadata_config: MetadataConfig
24
+ doc_id: Optional[str] = None
25
+ content_type: Optional[str] = None
26
+
27
+
28
+ class ProcessFilesParameters(ValidatedModel):
29
+ """Model for use with the process_files parser API."""
30
+
31
+ doc_ids: Optional[list[str]] = None
32
+ parser_config: ParserConfig
33
+ metadata_config: MetadataConfig
34
+
35
+
36
+ class GroupAuthorizationActions(str, Enum):
37
+ """Enum for use with the update_group_authorization API to specify the edit type."""
38
+
39
+ ADD = "add"
40
+ REMOVE = "remove"
41
+
42
+
43
+ class GroupAuthorizationInput(BaseModel):
44
+ """Model for use with the update_group_authorization API."""
45
+
46
+ document_ids: list[str]
47
+ authorized_groups: list[str]
48
+ action: GroupAuthorizationActions
@@ -0,0 +1,3 @@
1
+ from cohere_compass.clients.compass import * # noqa: F403
2
+ from cohere_compass.clients.parser import * # noqa: F403
3
+ from cohere_compass.clients.rbac import * # noqa: F403
@@ -0,0 +1,637 @@
1
+ import json
2
+ from typing import Optional, TypeVar
3
+
4
+ import requests
5
+ from pydantic import BaseModel
6
+ from requests import HTTPError
7
+
8
+ from cohere_compass.models.access_control import (
9
+ DetailedGroup,
10
+ DetailedRole,
11
+ DetailedUser,
12
+ Group,
13
+ GroupMembership,
14
+ GroupRole,
15
+ GroupsPage,
16
+ PageDirection,
17
+ PageInfo,
18
+ Role,
19
+ RolesPage,
20
+ User,
21
+ UsersPage,
22
+ UserWithToken,
23
+ )
24
+
25
+
26
+ class CompassRootClient:
27
+ """Client for interacting with Compass RBAC API V2 as a root user."""
28
+
29
+ def __init__(
30
+ self, compass_url: str, root_user_token: str, include_api_in_url: bool = True
31
+ ):
32
+ """
33
+ Initialize a new CompassRootClient.
34
+
35
+ :param compass_url: URL of the Compass instance.
36
+ :param root_user_token: Root user token for Compass instance.
37
+ :param include_api_in_url: Whether to include '/api' in the base URL.
38
+ Defaults to True.
39
+ """
40
+ self.base_url = (
41
+ compass_url
42
+ + ("/api" if include_api_in_url else "")
43
+ + "/security/admin/rbac"
44
+ )
45
+ self.headers = {
46
+ "Authorization": f"Bearer {root_user_token}",
47
+ "Content-Type": "application/json",
48
+ }
49
+
50
+ T = TypeVar("T", bound=BaseModel)
51
+ U = TypeVar("U", bound=BaseModel)
52
+ Headers = dict[str, str]
53
+
54
+ @staticmethod
55
+ def raise_for_status(response: requests.Response):
56
+ """
57
+ Raise an exception if the response status code is not in the 200 range.
58
+
59
+ :param response: Response object from the request.
60
+
61
+ :raises HTTPError: If the response status code is not in the 200 range.
62
+ """
63
+ http_error_msg = ""
64
+ if isinstance(response.reason, bytes):
65
+ # We attempt to decode utf-8 first because some servers
66
+ # choose to localize their reason strings. If the string
67
+ # isn't utf-8, we fall back to iso-8859-1 for all other encodings.
68
+ try:
69
+ reason = response.reason.decode("utf-8")
70
+ except UnicodeDecodeError:
71
+ reason = response.reason.decode("iso-8859-1")
72
+ else:
73
+ reason = response.content
74
+
75
+ if 400 <= response.status_code < 500:
76
+ http_error_msg = (
77
+ f"{response.status_code} Client Error: {reason} for url: {response.url}"
78
+ )
79
+
80
+ elif 500 <= response.status_code < 600:
81
+ http_error_msg = (
82
+ f"{response.status_code} Server Error: {reason} for url: {response.url}"
83
+ )
84
+
85
+ if http_error_msg:
86
+ raise HTTPError(http_error_msg, response=response)
87
+
88
+ @staticmethod
89
+ def _fetch_page(
90
+ url: str,
91
+ headers: Headers,
92
+ entity_response: type[T],
93
+ *,
94
+ filter: Optional[str] = None,
95
+ page_info: Optional[PageInfo] = None,
96
+ direction: Optional[PageDirection] = PageDirection.NEXT,
97
+ ) -> T:
98
+ params: dict[str, str] = {}
99
+ if filter is not None:
100
+ params["filter"] = filter
101
+
102
+ if page_info:
103
+ cursor_value = (
104
+ page_info.next
105
+ if direction == PageDirection.NEXT
106
+ else page_info.previous
107
+ )
108
+ if cursor_value is not None:
109
+ params["cursor"] = cursor_value
110
+ if page_info.filter is not None:
111
+ params["filter"] = page_info.filter
112
+
113
+ response = requests.get(url, headers=headers, params=params)
114
+ CompassRootClient.raise_for_status(response)
115
+ return entity_response.model_validate(response.json())
116
+
117
+ @staticmethod
118
+ def _fetch_entity(
119
+ url: str, headers: Headers, entity_response: type[T], entity_name: str
120
+ ) -> T:
121
+ response = requests.get(f"{url}/{entity_name}", headers=headers)
122
+ CompassRootClient.raise_for_status(response)
123
+ return entity_response.model_validate(response.json())
124
+
125
+ @staticmethod
126
+ def _create_entities(
127
+ url: str, headers: Headers, entity_request: list[T], entity_response: type[U]
128
+ ) -> list[U]:
129
+ response = requests.post(
130
+ url,
131
+ json=[json.loads(entity.model_dump_json()) for entity in entity_request],
132
+ headers=headers,
133
+ )
134
+ CompassRootClient.raise_for_status(response)
135
+ return [
136
+ entity_response.model_validate(response) for response in response.json()
137
+ ]
138
+
139
+ @staticmethod
140
+ def _update_entity(
141
+ url: str,
142
+ headers: Headers,
143
+ entity_name: str,
144
+ entity: BaseModel,
145
+ entity_response: type[U],
146
+ ) -> U:
147
+ response = requests.put(
148
+ f"{url}/{entity_name}",
149
+ json=json.loads(entity.model_dump_json()),
150
+ headers=headers,
151
+ )
152
+ CompassRootClient.raise_for_status(response)
153
+ return entity_response.model_validate(response.json())
154
+
155
+ @staticmethod
156
+ def _delete_entities(
157
+ url: str, headers: Headers, names: list[str], entity_response: type[U]
158
+ ) -> list[U]:
159
+ entities = ",".join(names)
160
+ response = requests.delete(f"{url}/{entities}", headers=headers)
161
+ CompassRootClient.raise_for_status(response)
162
+ return [entity_response.model_validate(entity) for entity in response.json()]
163
+
164
+ def get_users_page(
165
+ self,
166
+ *,
167
+ filter: Optional[str] = None,
168
+ page_info: Optional[PageInfo] = None,
169
+ direction: Optional[PageDirection] = None,
170
+ ) -> UsersPage:
171
+ """
172
+ Fetch a page of Users. Defaults to fetching the first page.
173
+
174
+ :param filter: Optional filter to apply to the user set.
175
+ :param page_info: Optional pagination information.
176
+ :param direction: Optional direction to paginate in. Defaults to NEXT.
177
+
178
+ Important notes:
179
+
180
+ When fetching the first page, the `page_info` parameter
181
+ should be `None`. When fetching subsequent pages, the `page_info` parameter
182
+ should be the `page_info` from the existing page.
183
+
184
+ If filter and page_info.filter are both None, all Users are eligible to be
185
+ fetched. If both are provided, the page_info.filter is used.
186
+
187
+ :return: Page of Users.
188
+ """
189
+ return self._fetch_page(
190
+ url=f"{self.base_url}/v2/users",
191
+ headers=self.headers,
192
+ entity_response=UsersPage,
193
+ filter=filter,
194
+ page_info=page_info,
195
+ direction=direction,
196
+ )
197
+
198
+ def create_users(self, users: list[User]) -> list[UserWithToken]:
199
+ """
200
+ Create new Users.
201
+
202
+ :param users: List of Users to create.
203
+
204
+ :return: List of created UserWithToken.
205
+ """
206
+ return self._create_entities(
207
+ url=f"{self.base_url}/v2/users",
208
+ headers=self.headers,
209
+ entity_request=users,
210
+ entity_response=UserWithToken,
211
+ )
212
+
213
+ def get_detailed_user(self, user_name: str) -> DetailedUser:
214
+ """
215
+ Get a detailed User.
216
+
217
+ :param user_name: Name of the User to get.
218
+
219
+ :return: DetailedUser containing the User and a Page of Groups plus PageInfo.
220
+ """
221
+ return self._fetch_entity(
222
+ url=f"{self.base_url}/v2/users",
223
+ headers=self.headers,
224
+ entity_response=DetailedUser,
225
+ entity_name=user_name,
226
+ )
227
+
228
+ def get_user_groups_page(
229
+ self,
230
+ user_name: str,
231
+ *,
232
+ filter: Optional[str] = None,
233
+ page_info: Optional[PageInfo] = None,
234
+ direction: Optional[PageDirection] = None,
235
+ ) -> GroupsPage:
236
+ """
237
+ Fetch a page of Groups a User is a Member of.
238
+
239
+ Defaults to fetching the first page.
240
+
241
+ :param user_name: Name of the User to fetch Groups for.
242
+ :param filter: Optional filter to apply to the Group set.
243
+ :param page_info: Optional pagination information.
244
+ :param direction: Optional direction to paginate in. Defaults to NEXT.
245
+
246
+ Important notes:
247
+
248
+ When fetching the first page, the `page_info` parameter
249
+ should be `None`. When fetching subsequent pages, the `page_info` parameter
250
+ should be the `page_info` from the existing page.
251
+
252
+ If filter and page_info.filter are both None, all Groups are eligible to be
253
+ fetched. If both are provided, the page_info.filter is used.
254
+
255
+ :return: Page of Groups for the User.
256
+ """
257
+ return self._fetch_page(
258
+ url=f"{self.base_url}/v2/users/{user_name}/groups",
259
+ headers=self.headers,
260
+ entity_response=GroupsPage,
261
+ filter=filter,
262
+ page_info=page_info,
263
+ direction=direction,
264
+ )
265
+
266
+ def delete_users(self, user_names: list[str]) -> list[User]:
267
+ """
268
+ Delete Users.
269
+
270
+ :param user_names: List of User names to delete.
271
+
272
+ :return: List of deleted Users.
273
+ """
274
+ return self._delete_entities(
275
+ url=f"{self.base_url}/v2/users",
276
+ headers=self.headers,
277
+ names=user_names,
278
+ entity_response=User,
279
+ )
280
+
281
+ def get_roles_page(
282
+ self,
283
+ *,
284
+ filter: Optional[str] = None,
285
+ page_info: Optional[PageInfo] = None,
286
+ direction: Optional[PageDirection] = None,
287
+ ) -> RolesPage:
288
+ """
289
+ Fetch a page of Roles. Defaults to fetching the first page.
290
+
291
+ :param filter: Optional filter to apply to the role set.
292
+ :param page_info: Optional pagination information.
293
+ :param direction: Optional direction to paginate in. Defaults to NEXT.
294
+
295
+ Important notes:
296
+
297
+ When fetching the first page, the `page_info` parameter
298
+ should be `None`. When fetching subsequent pages, the `page_info` parameter
299
+ should be the `page_info` from the existing page.
300
+
301
+ If filter and page_info.filter are both None, all Roles are eligible to be
302
+ fetched. If both are provided, the page_info.filter is used.
303
+
304
+ :return: Page of Roles.
305
+ """
306
+ return self._fetch_page(
307
+ url=f"{self.base_url}/v2/roles",
308
+ headers=self.headers,
309
+ entity_response=RolesPage,
310
+ filter=filter,
311
+ page_info=page_info,
312
+ direction=direction,
313
+ )
314
+
315
+ def create_roles(self, roles: list[Role]) -> list[Role]:
316
+ """
317
+ Create new Roles.
318
+
319
+ :param roles: List of Roles to create.
320
+
321
+ :return: List of created Roles.
322
+ """
323
+ return self._create_entities(
324
+ url=f"{self.base_url}/v2/roles",
325
+ headers=self.headers,
326
+ entity_request=roles,
327
+ entity_response=Role,
328
+ )
329
+
330
+ def get_detailed_role(self, role_name: str) -> DetailedRole:
331
+ """
332
+ Get a detailed Role.
333
+
334
+ :param role_name: Name of the Role to get.
335
+
336
+ :return: DetailedRole containing the Role and a Page of Groups plus PageInfo.
337
+ """
338
+ return self._fetch_entity(
339
+ url=f"{self.base_url}/v2/roles",
340
+ headers=self.headers,
341
+ entity_response=DetailedRole,
342
+ entity_name=role_name,
343
+ )
344
+
345
+ def get_role_groups_page(
346
+ self,
347
+ role_name: str,
348
+ *,
349
+ filter: Optional[str] = None,
350
+ page_info: Optional[PageInfo] = None,
351
+ direction: Optional[PageDirection] = None,
352
+ ) -> GroupsPage:
353
+ """
354
+ Fetch a page of Groups for a Role. Defaults to fetching the first page.
355
+
356
+ :param role_name: Name of the Role to fetch Groups for.
357
+ :param filter: Optional filter to apply to the Group set.
358
+ :param page_info: Optional pagination information.
359
+ :param direction: Optional direction to paginate in. Defaults to NEXT.
360
+
361
+ Important notes:
362
+
363
+ When fetching the first page, the `page_info` parameter
364
+ should be `None`. When fetching subsequent pages, the `page_info` parameter
365
+ should be the `page_info` from the existing page.
366
+
367
+ If filter and page_info.filter are both None, all Groups are eligible to be
368
+ fetched. If both are provided, the page_info.filter is used.
369
+
370
+ :return: Page of Groups for the Role.
371
+ """
372
+ return self._fetch_page(
373
+ url=f"{self.base_url}/v2/roles/{role_name}/groups",
374
+ headers=self.headers,
375
+ entity_response=GroupsPage,
376
+ filter=filter,
377
+ page_info=page_info,
378
+ direction=direction,
379
+ )
380
+
381
+ def update_role(self, role: Role) -> Role:
382
+ """
383
+ Update a Role.
384
+
385
+ :param role: Role to update.
386
+
387
+ :return: Updated Role.
388
+ """
389
+ response = requests.put(
390
+ f"{self.base_url}/v2/roles/{role.role_name}",
391
+ json=[json.loads(entity.model_dump_json()) for entity in role.policies],
392
+ headers=self.headers,
393
+ )
394
+ CompassRootClient.raise_for_status(response)
395
+ return Role.model_validate(response.json())
396
+
397
+ def delete_roles(self, role_names: list[str]) -> list[Role]:
398
+ """
399
+ Delete Roles.
400
+
401
+ :param role_names: List of Role names to delete.
402
+
403
+ :return: List of deleted Roles.
404
+ """
405
+ return self._delete_entities(
406
+ url=f"{self.base_url}/v2/roles",
407
+ headers=self.headers,
408
+ names=role_names,
409
+ entity_response=Role,
410
+ )
411
+
412
+ def get_groups_page(
413
+ self,
414
+ *,
415
+ filter: Optional[str] = None,
416
+ page_info: Optional[PageInfo] = None,
417
+ direction: Optional[PageDirection] = None,
418
+ ) -> GroupsPage:
419
+ """
420
+ Fetch a page of Groups. Defaults to fetching the first page.
421
+
422
+ :param filter: Optional filter to apply to the Group set.
423
+ :param page_info: Optional pagination information.
424
+ :param direction: Optional direction to paginate in. Defaults to NEXT.
425
+
426
+ Important notes:
427
+
428
+ When fetching the first page, the `page_info` parameter
429
+ should be `None`. When fetching subsequent pages, the `page_info` parameter
430
+ should be the `page_info` from the existing page.
431
+
432
+ If filter and page_info.filter are both None, all Groups are eligible to be
433
+ fetched. If both are provided, the page_info.filter is used.
434
+
435
+ :return: Page of Groups.
436
+ """
437
+ return self._fetch_page(
438
+ url=f"{self.base_url}/v2/groups",
439
+ headers=self.headers,
440
+ entity_response=GroupsPage,
441
+ filter=filter,
442
+ page_info=page_info,
443
+ direction=direction,
444
+ )
445
+
446
+ def create_groups(self, groups: list[Group]) -> list[Group]:
447
+ """
448
+ Create new Groups.
449
+
450
+ :param groups: List of Groups to create.
451
+
452
+ :return: List of created Groups.
453
+ """
454
+ return self._create_entities(
455
+ url=f"{self.base_url}/v2/groups",
456
+ headers=self.headers,
457
+ entity_request=groups,
458
+ entity_response=Group,
459
+ )
460
+
461
+ def get_detailed_group(self, group_name: str) -> DetailedGroup:
462
+ """
463
+ Get a detailed Group.
464
+
465
+ :param group_name: Name of the Group to get.
466
+
467
+ :return: DetailedGroup containing the Group and:
468
+ * a Page of Users plus PageInfo
469
+ * a Page of Roles plus PageInfo.
470
+ """
471
+ return self._fetch_entity(
472
+ url=f"{self.base_url}/v2/groups",
473
+ headers=self.headers,
474
+ entity_response=DetailedGroup,
475
+ entity_name=group_name,
476
+ )
477
+
478
+ def delete_groups(self, group_names: list[str]) -> list[Group]:
479
+ """
480
+ Delete Groups.
481
+
482
+ :param group_names: List of Group names to delete.
483
+
484
+ :return: List of deleted Groups.
485
+ """
486
+ return self._delete_entities(
487
+ url=f"{self.base_url}/v2/groups",
488
+ headers=self.headers,
489
+ names=group_names,
490
+ entity_response=Group,
491
+ )
492
+
493
+ def add_members_to_group(
494
+ self, group_name: str, user_names: list[str]
495
+ ) -> list[GroupMembership]:
496
+ """
497
+ Add Users to a Group.
498
+
499
+ :param group_name: Name of the Group to add Users to.
500
+ :param user_names: List of User names added to the Group.
501
+
502
+ :return: List of added GroupMemberships.
503
+ """
504
+ response = requests.post(
505
+ f"{self.base_url}/v2/groups/{group_name}/users",
506
+ json=[{"user_name": user_name} for user_name in user_names],
507
+ headers=self.headers,
508
+ )
509
+ CompassRootClient.raise_for_status(response)
510
+ return [GroupMembership.model_validate(member) for member in response.json()]
511
+
512
+ def remove_members_from_group(
513
+ self, group_name: str, user_names: list[str]
514
+ ) -> list[GroupMembership]:
515
+ """
516
+ Remove Users from a Group.
517
+
518
+ :param group_name: Name of the Group to remove Users from.
519
+ :param user_names: List of User names removed from the Group.
520
+
521
+ :return: List of removed GroupMemberships.
522
+ """
523
+ return self._delete_entities(
524
+ url=f"{self.base_url}/v2/groups/{group_name}/users",
525
+ headers=self.headers,
526
+ names=user_names,
527
+ entity_response=GroupMembership,
528
+ )
529
+
530
+ def get_group_members_page(
531
+ self,
532
+ group_name: str,
533
+ *,
534
+ filter: Optional[str] = None,
535
+ page_info: Optional[PageInfo] = None,
536
+ direction: Optional[PageDirection] = None,
537
+ ) -> UsersPage:
538
+ """
539
+ Fetch a page of Users in a Group. Defaults to fetching the first page.
540
+
541
+ :param group_name: Name of the Group to fetch Users for.
542
+ :param filter: Optional filter to apply to the User set.
543
+ :param page_info: Optional pagination information.
544
+ :param direction: Optional direction to paginate in. Defaults to NEXT.
545
+
546
+ Important notes:
547
+
548
+ When fetching the first page, the `page_info` parameter
549
+ should be `None`. When fetching subsequent pages, the `page_info` parameter
550
+ should be the `page_info` from the existing page.
551
+
552
+ If filter and page_info.filter are both None, all Users are eligible to be
553
+ fetched. If both are provided, the page_info.filter is used.
554
+
555
+ :return: Page of Users in the Group.
556
+ """
557
+ return self._fetch_page(
558
+ url=f"{self.base_url}/v2/groups/{group_name}/users",
559
+ headers=self.headers,
560
+ entity_response=UsersPage,
561
+ filter=filter,
562
+ page_info=page_info,
563
+ direction=direction,
564
+ )
565
+
566
+ def add_roles_to_group(
567
+ self, group_name: str, role_names: list[str]
568
+ ) -> list[GroupRole]:
569
+ """
570
+ Add Roles to a Group.
571
+
572
+ :param group_name: Name of the Group to add Roles to.
573
+ :param role_names: List of Role names added to the Group.
574
+
575
+ :return: List of added GroupRoles.
576
+ """
577
+ response = requests.post(
578
+ f"{self.base_url}/v2/groups/{group_name}/roles",
579
+ json=[{"role_name": role_name} for role_name in role_names],
580
+ headers=self.headers,
581
+ )
582
+ CompassRootClient.raise_for_status(response)
583
+ return [GroupRole.model_validate(member) for member in response.json()]
584
+
585
+ def remove_roles_from_group(
586
+ self, group_name: str, role_names: list[str]
587
+ ) -> list[GroupRole]:
588
+ """
589
+ Remove Roles from a Group.
590
+
591
+ :param group_name: Name of the Group to remove Roles from.
592
+ :param role_names: List of Role names removed from the Group.
593
+
594
+ :return: List of removed GroupRoles.
595
+ """
596
+ return self._delete_entities(
597
+ url=f"{self.base_url}/v2/groups/{group_name}/roles",
598
+ headers=self.headers,
599
+ names=role_names,
600
+ entity_response=GroupRole,
601
+ )
602
+
603
+ def get_group_roles_page(
604
+ self,
605
+ group_name: str,
606
+ *,
607
+ filter: Optional[str] = None,
608
+ page_info: Optional[PageInfo] = None,
609
+ direction: Optional[PageDirection] = None,
610
+ ) -> RolesPage:
611
+ """
612
+ Fetch a page of Roles in a Group. Defaults to fetching the first page.
613
+
614
+ :param group_name: Name of the Group to fetch Roles for.
615
+ :param filter: Optional filter to apply to the Role set.
616
+ :param page_info: Optional pagination information.
617
+ :param direction: Optional direction to paginate in. Defaults to NEXT.
618
+
619
+ Important notes:
620
+
621
+ When fetching the first page, the `page_info` parameter
622
+ should be `None`. When fetching subsequent pages, the `page_info` parameter
623
+ should be the `page_info` from the existing page.
624
+
625
+ If filter and page_info.filter are both None, all Roles are eligible to be
626
+ fetched. If both are provided, the page_info.filter is used.
627
+
628
+ :return: Page of Roles in the Group.
629
+ """
630
+ return self._fetch_page(
631
+ url=f"{self.base_url}/v2/groups/{group_name}/roles",
632
+ headers=self.headers,
633
+ entity_response=RolesPage,
634
+ filter=filter,
635
+ page_info=page_info,
636
+ direction=direction,
637
+ )