personal_knowledge_library 3.0.0__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.

Potentially problematic release.


This version of personal_knowledge_library might be problematic. Click here for more details.

Files changed (42) hide show
  1. knowledge/__init__.py +91 -0
  2. knowledge/base/__init__.py +22 -0
  3. knowledge/base/access.py +167 -0
  4. knowledge/base/entity.py +267 -0
  5. knowledge/base/language.py +27 -0
  6. knowledge/base/ontology.py +2734 -0
  7. knowledge/base/search.py +473 -0
  8. knowledge/base/tenant.py +192 -0
  9. knowledge/nel/__init__.py +11 -0
  10. knowledge/nel/base.py +495 -0
  11. knowledge/nel/engine.py +123 -0
  12. knowledge/ontomapping/__init__.py +667 -0
  13. knowledge/ontomapping/manager.py +320 -0
  14. knowledge/public/__init__.py +27 -0
  15. knowledge/public/cache.py +115 -0
  16. knowledge/public/helper.py +373 -0
  17. knowledge/public/relations.py +128 -0
  18. knowledge/public/wikidata.py +1324 -0
  19. knowledge/services/__init__.py +128 -0
  20. knowledge/services/asyncio/__init__.py +7 -0
  21. knowledge/services/asyncio/base.py +458 -0
  22. knowledge/services/asyncio/graph.py +1420 -0
  23. knowledge/services/asyncio/group.py +450 -0
  24. knowledge/services/asyncio/search.py +439 -0
  25. knowledge/services/asyncio/users.py +270 -0
  26. knowledge/services/base.py +533 -0
  27. knowledge/services/graph.py +1897 -0
  28. knowledge/services/group.py +819 -0
  29. knowledge/services/helper.py +142 -0
  30. knowledge/services/ontology.py +1234 -0
  31. knowledge/services/search.py +488 -0
  32. knowledge/services/session.py +444 -0
  33. knowledge/services/tenant.py +281 -0
  34. knowledge/services/users.py +445 -0
  35. knowledge/utils/__init__.py +10 -0
  36. knowledge/utils/graph.py +417 -0
  37. knowledge/utils/wikidata.py +197 -0
  38. knowledge/utils/wikipedia.py +175 -0
  39. personal_knowledge_library-3.0.0.dist-info/LICENSE +201 -0
  40. personal_knowledge_library-3.0.0.dist-info/METADATA +1163 -0
  41. personal_knowledge_library-3.0.0.dist-info/RECORD +42 -0
  42. personal_knowledge_library-3.0.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,439 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright © 2024 Wacom. All rights reserved.
3
+ import asyncio
4
+ from typing import Dict, Any, Optional, List
5
+
6
+ import orjson
7
+
8
+ from knowledge.base.language import LocaleCode
9
+ from knowledge.base.search import DocumentSearchResponse, LabelMatchingResponse, VectorDBDocument
10
+ from knowledge.services import (
11
+ DEFAULT_TIMEOUT,
12
+ AUTHORIZATION_HEADER_FLAG,
13
+ APPLICATION_JSON_HEADER,
14
+ CONTENT_TYPE_HEADER_FLAG,
15
+ USER_AGENT_HEADER_FLAG,
16
+ )
17
+ from knowledge.services.asyncio.base import AsyncServiceAPIClient, handle_error
18
+
19
+
20
+ class AsyncSemanticSearchClient(AsyncServiceAPIClient):
21
+ """
22
+ Semantic Search Client
23
+ ======================
24
+ Client for searching semantically similar documents and labels.
25
+
26
+ Parameters
27
+ ----------
28
+ service_url: str
29
+ Service URL for the client.
30
+ service_endpoint: str (Default:= 'vector/v1')
31
+ Service endpoint for the client.
32
+ """
33
+
34
+ def __init__(self, service_url: str, service_endpoint: str = "vector/api/v1"):
35
+ super().__init__("Async Semantic Search ", service_url, service_endpoint)
36
+
37
+ async def retrieve_document_chunks(
38
+ self, locale: LocaleCode, uri: str, auth_key: Optional[str] = None
39
+ ) -> List[VectorDBDocument]:
40
+ """
41
+ Retrieve document chunks from vector database. The service is automatically chunking the document into
42
+ smaller parts. The chunks are returned as a list of dictionaries, with metadata and content.
43
+
44
+ Parameters
45
+ ----------
46
+ locale: LocaleCode
47
+ Locale
48
+ uri: str
49
+ URI of the document
50
+ auth_key: Optional[str] (Default:= None)
51
+ If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
52
+
53
+ Returns
54
+ -------
55
+ document: Dict[str, Any]
56
+ List of document chunks with metadata and content related to the document.
57
+
58
+ Raises
59
+ ------
60
+ WacomServiceException
61
+ If the request fails.
62
+ """
63
+ if auth_key is None:
64
+ auth_key, _ = await self.handle_token()
65
+ url: str = f"{self.service_base_url}documents/"
66
+
67
+ headers: Dict[str, str] = {
68
+ USER_AGENT_HEADER_FLAG: self.user_agent,
69
+ CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
70
+ AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
71
+ }
72
+ async with self.__async_session__() as session:
73
+ async with session.get(
74
+ url, params={"locale": locale, "uri": uri}, headers=headers, timeout=DEFAULT_TIMEOUT
75
+ ) as response:
76
+ if response.ok:
77
+ docs: List[VectorDBDocument] = [
78
+ VectorDBDocument(vec_doc) for vec_doc in await response.json(loads=orjson.loads)
79
+ ]
80
+ else:
81
+ raise await handle_error(
82
+ "Failed to retrieve the document.",
83
+ response,
84
+ headers=headers,
85
+ parameters={"locale": locale, "uri": uri},
86
+ )
87
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
88
+ return docs
89
+
90
+ async def retrieve_labels(
91
+ self, locale: LocaleCode, uri: str, auth_key: Optional[str] = None
92
+ ) -> List[VectorDBDocument]:
93
+ """
94
+ Retrieve labels from vector database.
95
+
96
+ Parameters
97
+ ----------
98
+ locale: LocaleCode
99
+ Locale
100
+ uri: str
101
+ URI of the document
102
+ auth_key: Optional[str] (Default:= None)
103
+ If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
104
+
105
+ Returns
106
+ -------
107
+ document: List[VectorDBDocument]
108
+ List of labels with metadata and content related to the entity with uri.
109
+
110
+ Raises
111
+ ------
112
+ WacomServiceException
113
+ If the request fails.
114
+ """
115
+ if auth_key is None:
116
+ auth_key, _ = await self.handle_token()
117
+ url: str = f"{self.service_base_url}labels/"
118
+
119
+ headers: Dict[str, str] = {
120
+ USER_AGENT_HEADER_FLAG: self.user_agent,
121
+ CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
122
+ AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
123
+ }
124
+ async with self.__async_session__() as session:
125
+ async with session.get(
126
+ url, params={"locale": locale, "uri": uri}, headers=headers, timeout=DEFAULT_TIMEOUT
127
+ ) as response:
128
+ if response.ok:
129
+ docs: List[VectorDBDocument] = [
130
+ VectorDBDocument(vec_doc) for vec_doc in await response.json(loads=orjson.loads)
131
+ ]
132
+ else:
133
+ raise await handle_error(
134
+ "Failed to retrieve the document.",
135
+ response,
136
+ headers=headers,
137
+ parameters={"locale": locale, "uri": uri},
138
+ )
139
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
140
+ return docs
141
+
142
+ async def count_documents(
143
+ self, locale: LocaleCode, concept_type: Optional[str] = None, auth_key: Optional[str] = None
144
+ ) -> int:
145
+ """
146
+ Count all documents for a tenant.
147
+
148
+ Parameters
149
+ ----------
150
+ locale: str
151
+ Locale
152
+ concept_type: Optional[str] (Default:= None)
153
+ Concept type.
154
+ auth_key: Optional[str] (Default:= None)
155
+ If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
156
+
157
+ Returns
158
+ -------
159
+ number_of_docs: int
160
+ Number of documents.
161
+
162
+ Raises
163
+ ------
164
+ WacomServiceException
165
+ If the request fails.
166
+ """
167
+ if auth_key is None:
168
+ auth_key, _ = await self.handle_token()
169
+ url: str = f"{self.service_base_url}documents/count/"
170
+ headers: Dict[str, str] = {
171
+ USER_AGENT_HEADER_FLAG: self.user_agent,
172
+ CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
173
+ AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
174
+ }
175
+ params: Dict[str, Any] = {"locale": locale}
176
+ if concept_type:
177
+ params["concept_type"] = concept_type
178
+ async with self.__async_session__() as session:
179
+ async with session.get(url, params=params, headers=headers) as response:
180
+ if response.ok:
181
+ count: int = (await response.json(loads=orjson.loads)).get("count", 0)
182
+ else:
183
+ raise await handle_error(
184
+ "Counting documents failed.", response, headers=headers, parameters={"locale": locale}
185
+ )
186
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
187
+ return count
188
+
189
+ async def count_documents_filter(
190
+ self, locale: LocaleCode, filters: Dict[str, Any], auth_key: Optional[str] = None
191
+ ) -> int:
192
+ """
193
+ Count all documents for a tenant using a filter.
194
+
195
+ Parameters
196
+ ----------
197
+ locale: str
198
+ Locale
199
+ filters: Dict[str, Any]
200
+ Filters for the search
201
+ auth_key: Optional[str] (Default:= None)
202
+ If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
203
+
204
+ Returns
205
+ -------
206
+ number_of_docs: int
207
+ Number of documents.
208
+
209
+ Raises
210
+ ------
211
+ WacomServiceException
212
+ If the request fails.
213
+ """
214
+ if auth_key is None:
215
+ auth_key, _ = await self.handle_token()
216
+ url: str = f"{self.service_base_url}documents/count/filter"
217
+ headers: Dict[str, str] = {
218
+ USER_AGENT_HEADER_FLAG: self.user_agent,
219
+ CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
220
+ AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
221
+ }
222
+ async with self.__async_session__() as session:
223
+ async with session.post(url, json={"locale": locale, "filter": filters}, headers=headers) as response:
224
+ if response.ok:
225
+ count: int = (await response.json(loads=orjson.loads)).get("count", 0)
226
+ else:
227
+ raise await handle_error(
228
+ "Counting documents failed.",
229
+ response,
230
+ headers=headers,
231
+ parameters={"locale": locale, "filter": filters},
232
+ )
233
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
234
+ return count
235
+
236
+ async def count_labels(
237
+ self, locale: str, concept_type: Optional[str] = None, auth_key: Optional[str] = None
238
+ ) -> int:
239
+ """
240
+ Count all labels entries for a tenant.
241
+
242
+ Parameters
243
+ ----------
244
+ locale: str
245
+ Locale
246
+ concept_type: Optional[str] (Default:= None)
247
+ Concept type.
248
+ auth_key: Optional[str] (Default:= None)
249
+ If auth key is provided, it will be used for the request.
250
+
251
+ Returns
252
+ -------
253
+ count: int
254
+ Number of words.
255
+
256
+ Raises
257
+ ------
258
+ WacomServiceException
259
+ If the request fails.
260
+ """
261
+ if auth_key is None:
262
+ auth_key, _ = await self.handle_token()
263
+ url: str = f"{self.service_base_url}labels/count/"
264
+ headers: Dict[str, str] = {
265
+ USER_AGENT_HEADER_FLAG: self.user_agent,
266
+ CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
267
+ AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
268
+ }
269
+ params: Dict[str, Any] = {"locale": locale}
270
+ if concept_type:
271
+ params["concept_type"] = concept_type
272
+ async with self.__async_session__() as session:
273
+ async with session.get(url, params=params, headers=headers) as response:
274
+ if response.ok:
275
+ count: int = (await response.json(loads=orjson.loads)).get("count", 0)
276
+ else:
277
+ raise await handle_error(
278
+ "Counting labels failed.", response, headers=headers, parameters={"locale": locale}
279
+ )
280
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
281
+ return count
282
+
283
+ async def count_labels_filter(
284
+ self, locale: LocaleCode, filters: Dict[str, Any], auth_key: Optional[str] = None
285
+ ) -> int:
286
+ """
287
+ Count all labels for a tenant using a filter.
288
+
289
+ Parameters
290
+ ----------
291
+ locale: str
292
+ Locale
293
+ filters: Dict[str, Any]
294
+ Filters for the search
295
+ auth_key: Optional[str] (Default:= None)
296
+ If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
297
+
298
+ Returns
299
+ -------
300
+ number_of_docs: int
301
+ Number of documents.
302
+
303
+ Raises
304
+ ------
305
+ WacomServiceException
306
+ If the request fails.
307
+ """
308
+ if auth_key is None:
309
+ auth_key, _ = await self.handle_token()
310
+ url: str = f"{self.service_base_url}labels/count/filter"
311
+ headers: Dict[str, str] = {
312
+ USER_AGENT_HEADER_FLAG: self.user_agent,
313
+ CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
314
+ AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
315
+ }
316
+ async with self.__async_session__() as session:
317
+ async with session.post(url, json={"locale": locale, "filter": filters}, headers=headers) as response:
318
+ if response.ok:
319
+ count: int = (await response.json(loads=orjson.loads)).get("count", 0)
320
+ else:
321
+ raise await handle_error(
322
+ "Counting documents failed.",
323
+ response,
324
+ headers=headers,
325
+ parameters={"locale": locale, "filter": filters},
326
+ )
327
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
328
+ return count
329
+
330
+ async def document_search(
331
+ self,
332
+ query: str,
333
+ locale: str,
334
+ filters: Optional[Dict[str, Any]] = None,
335
+ max_results: int = 10,
336
+ auth_key: Optional[str] = None,
337
+ ) -> DocumentSearchResponse:
338
+ """
339
+ Async Semantic search.
340
+
341
+ Parameters
342
+ ----------
343
+ query: str
344
+ Query text for the search
345
+ locale: str
346
+ Locale of the text
347
+ filters: Optional[Dict[str, Any]] = None
348
+ Filters for the search
349
+ max_results: int
350
+ Maximum number of results
351
+ auth_key: Optional[str] (Default:= None)
352
+ If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
353
+
354
+ Returns
355
+ -------
356
+ response: DocumentSearchResponse
357
+ Search results response.
358
+
359
+ Raises
360
+ ------
361
+ WacomServiceException
362
+ If the request fails.
363
+ """
364
+ if auth_key is None:
365
+ auth_key, _ = await self.handle_token()
366
+ url: str = f"{self.service_base_url}documents/search/"
367
+ headers: Dict[str, str] = {
368
+ USER_AGENT_HEADER_FLAG: self.user_agent,
369
+ CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
370
+ AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
371
+ }
372
+ params: Dict[str, Any] = {
373
+ "query": query,
374
+ "metadata": filters if filters else {},
375
+ "locale": locale,
376
+ "max_results": max_results,
377
+ }
378
+ async with self.__async_session__() as session:
379
+ async with session.post(url, headers=headers, json=params) as response:
380
+ if response.ok:
381
+ response_dict: Dict[str, Any] = await response.json(loads=orjson.loads)
382
+ else:
383
+ raise await handle_error("Semantic Search failed.", response, headers=headers, parameters=params)
384
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
385
+ return DocumentSearchResponse.from_dict(response_dict)
386
+
387
+ async def labels_search(
388
+ self,
389
+ query: str,
390
+ locale: str,
391
+ filters: Optional[Dict[str, Any]] = None,
392
+ max_results: int = 10,
393
+ auth_key: Optional[str] = None,
394
+ ) -> LabelMatchingResponse:
395
+ """
396
+ Async search for semantically similar labels.
397
+
398
+ Parameters
399
+ ----------
400
+ query: str
401
+ Query text for the search
402
+ locale: str
403
+ Locale of the text
404
+ filters: Optional[Dict[str, Any]] = None
405
+ Filters for the search
406
+ max_results: int
407
+ Maximum number of results
408
+ auth_key: Optional[str] (Default:= None)
409
+ If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
410
+
411
+ Returns
412
+ -------
413
+ response: LabelMatchingResponse
414
+ Search results response.
415
+ """
416
+ if auth_key is None:
417
+ auth_key, _ = await self.handle_token()
418
+ url: str = f"{self.service_base_url}labels/match/"
419
+ headers: Dict[str, str] = {
420
+ USER_AGENT_HEADER_FLAG: self.user_agent,
421
+ CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
422
+ AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
423
+ }
424
+ params: Dict[str, Any] = {
425
+ "query": query,
426
+ "metadata": filters if filters else {},
427
+ "locale": locale,
428
+ "max_results": max_results,
429
+ }
430
+ async with self.__async_session__() as session:
431
+ async with session.post(url, headers=headers, json=params) as response:
432
+ if response.ok:
433
+ response_dict: Dict[str, Any] = await response.json(loads=orjson.loads)
434
+ else:
435
+ raise await handle_error(
436
+ "Label fuzzy matching failed.", response, headers=headers, parameters=params
437
+ )
438
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
439
+ return LabelMatchingResponse.from_dict(response_dict)
@@ -0,0 +1,270 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright © 2024 Wacom. All rights reserved.
3
+ import asyncio
4
+ from datetime import datetime
5
+ from typing import Any, Union, Dict, List, Tuple
6
+
7
+ import orjson
8
+
9
+ from knowledge import logger
10
+ from knowledge.services import APPLICATION_JSON_HEADER, EXPIRATION_DATE_TAG
11
+ from knowledge.services.asyncio.base import AsyncServiceAPIClient, handle_error
12
+ from knowledge.services.base import WacomServiceAPIClient
13
+ from knowledge.services.users import (
14
+ UserRole,
15
+ USER_AGENT_TAG,
16
+ TENANT_API_KEY_FLAG,
17
+ OFFSET_TAG,
18
+ LIMIT_TAG,
19
+ User,
20
+ USER_ID_TAG,
21
+ EXTERNAL_USER_ID_TAG,
22
+ FORCE_TAG,
23
+ ROLES_TAG,
24
+ META_DATA_TAG,
25
+ CONTENT_TYPE_FLAG,
26
+ DEFAULT_TIMEOUT,
27
+ INTERNAL_USER_ID_TAG,
28
+ )
29
+
30
+
31
+ class AsyncUserManagementService(AsyncServiceAPIClient):
32
+ """
33
+ Async User-Management Service API
34
+ ---------------------------------
35
+ Functionality:
36
+ - List all users
37
+ - Create / update / delete users
38
+
39
+ Parameters
40
+ ----------
41
+ application_name: str
42
+ Name of the application
43
+ service_url: str
44
+ URL of the service
45
+ service_endpoint: str
46
+ Base endpoint
47
+ """
48
+
49
+ USER_DETAILS_ENDPOINT: str = f"{WacomServiceAPIClient.USER_ENDPOINT}/internal-id"
50
+
51
+ def __init__(
52
+ self,
53
+ application_name: str,
54
+ service_url: str = WacomServiceAPIClient.SERVICE_URL,
55
+ service_endpoint: str = "graph/v1",
56
+ ):
57
+ super().__init__(application_name=application_name, service_url=service_url, service_endpoint=service_endpoint)
58
+
59
+ # ------------------------------------------ Users handling --------------------------------------------------------
60
+
61
+ async def create_user(
62
+ self, tenant_key: str, external_id: str, meta_data: Dict[str, str] = None, roles: List[UserRole] = None
63
+ ) -> Tuple[User, str, str, datetime]:
64
+ """
65
+ Creates user for a tenant.
66
+
67
+ Parameters
68
+ ----------
69
+ tenant_key: str -
70
+ API key for tenant
71
+ external_id: str -
72
+ External id of user identification service.
73
+ meta_data: Dict[str, str]
74
+ Meta-data dictionary.
75
+ roles: List[UserRole]
76
+ List of roles.
77
+
78
+ Returns
79
+ -------
80
+ user: User
81
+ Instance of the user
82
+ token: str
83
+ Auth token for user
84
+ refresh_key: str
85
+ Refresh token
86
+ expiration_time: datetime
87
+ Expiration time
88
+ Raises
89
+ ------
90
+ WacomServiceException
91
+ If the tenant service returns an error code.
92
+ """
93
+ url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_ENDPOINT}"
94
+ headers: dict = {
95
+ USER_AGENT_TAG: self.user_agent,
96
+ TENANT_API_KEY_FLAG: tenant_key,
97
+ CONTENT_TYPE_FLAG: "application/json",
98
+ }
99
+ payload: dict = {
100
+ EXTERNAL_USER_ID_TAG: external_id,
101
+ META_DATA_TAG: meta_data if meta_data is not None else {},
102
+ ROLES_TAG: [r.value for r in roles] if roles is not None else [UserRole.USER.value],
103
+ }
104
+ async with self.__async_session__() as session:
105
+ async with session.post(
106
+ url, headers=headers, json=payload, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
107
+ ) as response:
108
+ if response.ok:
109
+ results: Dict[str, Union[str, Dict[str, str], List[str]]] = await response.json(loads=orjson.loads)
110
+ try:
111
+ date_object: datetime = datetime.fromisoformat(results["token"][EXPIRATION_DATE_TAG])
112
+ except (TypeError, ValueError) as _:
113
+ date_object: datetime = datetime.now()
114
+ logger.warning(f'Parsing of expiration date failed. {results["token"][EXPIRATION_DATE_TAG]}')
115
+
116
+ else:
117
+ raise await handle_error("Failed to create the user.", response, headers=headers, payload=payload)
118
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
119
+ return (
120
+ User.parse(results["user"]),
121
+ results["token"]["accessToken"],
122
+ results["token"]["refreshToken"],
123
+ date_object,
124
+ )
125
+
126
+ async def update_user(
127
+ self,
128
+ tenant_key: str,
129
+ internal_id: str,
130
+ external_id: str,
131
+ meta_data: Dict[str, str] = None,
132
+ roles: List[UserRole] = None,
133
+ ):
134
+ """Updates user for a tenant.
135
+
136
+ Parameters
137
+ ----------
138
+ tenant_key: str
139
+ API key for tenant
140
+ internal_id: str
141
+ Internal id of semantic service.
142
+ external_id: str
143
+ External id of user identification service.
144
+ meta_data: Dict[str, str]
145
+ Meta-data dictionary.
146
+ roles: List[UserRole]
147
+ List of roles.
148
+
149
+ Raises
150
+ ------
151
+ WacomServiceException
152
+ If the tenant service returns an error code.
153
+ """
154
+ url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_ENDPOINT}"
155
+ headers: Dict[str, str] = {
156
+ USER_AGENT_TAG: self.user_agent,
157
+ TENANT_API_KEY_FLAG: tenant_key,
158
+ CONTENT_TYPE_FLAG: APPLICATION_JSON_HEADER,
159
+ }
160
+ payload: Dict[str, str] = {
161
+ META_DATA_TAG: meta_data if meta_data is not None else {},
162
+ ROLES_TAG: [r.value for r in roles] if roles is not None else [UserRole.USER.value],
163
+ }
164
+ params: Dict[str, str] = {USER_ID_TAG: internal_id, EXTERNAL_USER_ID_TAG: external_id}
165
+ async with self.__async_session__() as session:
166
+ async with session.patch(
167
+ url, headers=headers, json=payload, params=params, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
168
+ ) as response:
169
+ if not response.ok:
170
+ raise await handle_error("Failed to update the user.", response, headers=headers, payload=payload)
171
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
172
+
173
+ async def delete_user(self, tenant_key: str, external_id: str, internal_id: str, force: bool = False):
174
+ """Deletes user from tenant.
175
+
176
+ Parameters
177
+ ----------
178
+ tenant_key: str
179
+ API key for tenant
180
+ external_id: str
181
+ External id of user identification service.
182
+ internal_id: str
183
+ Internal id of user.
184
+ force: bool
185
+ If set to true removes all user data including groups and entities.
186
+
187
+ Raises
188
+ ------
189
+ WacomServiceException
190
+ If the tenant service returns an error code.
191
+ """
192
+ url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_ENDPOINT}"
193
+ headers: Dict[str, str] = {USER_AGENT_TAG: self.user_agent, TENANT_API_KEY_FLAG: tenant_key}
194
+ params: Dict[str, str] = {USER_ID_TAG: internal_id, EXTERNAL_USER_ID_TAG: external_id, FORCE_TAG: str(force)}
195
+ async with self.__async_session__() as session:
196
+ async with session.delete(
197
+ url, headers=headers, params=params, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
198
+ ) as response:
199
+ if not response.ok:
200
+ raise await handle_error("Failed to delete the user.", response, headers=headers)
201
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
202
+
203
+ async def user_internal_id(self, tenant_key: str, external_id: str) -> str:
204
+ """User internal id.
205
+
206
+ Parameters
207
+ ----------
208
+ tenant_key: str
209
+ API key for tenant
210
+ external_id: str
211
+ External id of user
212
+
213
+ Returns
214
+ -------
215
+ internal_user_id: str
216
+ Internal id of users
217
+
218
+ Raises
219
+ ------
220
+ WacomServiceException
221
+ If the tenant service returns an error code.
222
+ """
223
+ url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_DETAILS_ENDPOINT}"
224
+ headers: dict = {USER_AGENT_TAG: self.user_agent, TENANT_API_KEY_FLAG: tenant_key}
225
+ parameters: Dict[str, str] = {EXTERNAL_USER_ID_TAG: external_id}
226
+ async with self.__async_session__() as session:
227
+ async with session.get(
228
+ url, headers=headers, params=parameters, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
229
+ ) as response:
230
+ if response.ok:
231
+ response_dict: Dict[str, Any] = await response.json(loads=orjson.loads)
232
+ else:
233
+ raise await handle_error("Failed to get the user.", response, headers=headers)
234
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
235
+ return response_dict[INTERNAL_USER_ID_TAG]
236
+
237
+ async def listing_users(self, tenant_key: str, offset: int = 0, limit: int = 20) -> List[User]:
238
+ """
239
+ Listing all users configured for this instance.
240
+
241
+ Parameters
242
+ ----------
243
+ tenant_key: str
244
+ API key for tenant
245
+ offset: int - [optional]
246
+ Offset value to define starting position in list. [DEFAULT:= 0]
247
+ limit: int - [optional]
248
+ Define the limit of the list size. [DEFAULT:= 20]
249
+
250
+ Returns
251
+ -------
252
+ user: List[User]
253
+ List of users.
254
+ """
255
+ url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_ENDPOINT}"
256
+ headers: Dict[str, str] = {USER_AGENT_TAG: self.user_agent, TENANT_API_KEY_FLAG: tenant_key}
257
+ params: Dict[str, str] = {OFFSET_TAG: offset, LIMIT_TAG: limit}
258
+ async with self.__async_session__() as session:
259
+ async with session.get(
260
+ url, headers=headers, params=params, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
261
+ ) as response:
262
+ if response.ok:
263
+ users: List[Dict[str, Any]] = await response.json(loads=orjson.loads)
264
+ results: List[User] = []
265
+ for u in users:
266
+ results.append(User.parse(u))
267
+ else:
268
+ await handle_error("Listing of users failed.", response, headers=headers, parameters=params)
269
+ await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
270
+ return results