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.
- knowledge/__init__.py +91 -0
- knowledge/base/__init__.py +22 -0
- knowledge/base/access.py +167 -0
- knowledge/base/entity.py +267 -0
- knowledge/base/language.py +27 -0
- knowledge/base/ontology.py +2734 -0
- knowledge/base/search.py +473 -0
- knowledge/base/tenant.py +192 -0
- knowledge/nel/__init__.py +11 -0
- knowledge/nel/base.py +495 -0
- knowledge/nel/engine.py +123 -0
- knowledge/ontomapping/__init__.py +667 -0
- knowledge/ontomapping/manager.py +320 -0
- knowledge/public/__init__.py +27 -0
- knowledge/public/cache.py +115 -0
- knowledge/public/helper.py +373 -0
- knowledge/public/relations.py +128 -0
- knowledge/public/wikidata.py +1324 -0
- knowledge/services/__init__.py +128 -0
- knowledge/services/asyncio/__init__.py +7 -0
- knowledge/services/asyncio/base.py +458 -0
- knowledge/services/asyncio/graph.py +1420 -0
- knowledge/services/asyncio/group.py +450 -0
- knowledge/services/asyncio/search.py +439 -0
- knowledge/services/asyncio/users.py +270 -0
- knowledge/services/base.py +533 -0
- knowledge/services/graph.py +1897 -0
- knowledge/services/group.py +819 -0
- knowledge/services/helper.py +142 -0
- knowledge/services/ontology.py +1234 -0
- knowledge/services/search.py +488 -0
- knowledge/services/session.py +444 -0
- knowledge/services/tenant.py +281 -0
- knowledge/services/users.py +445 -0
- knowledge/utils/__init__.py +10 -0
- knowledge/utils/graph.py +417 -0
- knowledge/utils/wikidata.py +197 -0
- knowledge/utils/wikipedia.py +175 -0
- personal_knowledge_library-3.0.0.dist-info/LICENSE +201 -0
- personal_knowledge_library-3.0.0.dist-info/METADATA +1163 -0
- personal_knowledge_library-3.0.0.dist-info/RECORD +42 -0
- personal_knowledge_library-3.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright © 2021-present Wacom. All rights reserved.
|
|
3
|
+
import sys
|
|
4
|
+
from abc import ABC
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any, Tuple, Dict, Optional, Union
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
from requests import Response
|
|
10
|
+
|
|
11
|
+
from knowledge import __version__, logger
|
|
12
|
+
from knowledge.services import DEFAULT_TIMEOUT
|
|
13
|
+
from knowledge.services import (
|
|
14
|
+
USER_AGENT_HEADER_FLAG,
|
|
15
|
+
TENANT_API_KEY,
|
|
16
|
+
CONTENT_TYPE_HEADER_FLAG,
|
|
17
|
+
REFRESH_TOKEN_TAG,
|
|
18
|
+
EXPIRATION_DATE_TAG,
|
|
19
|
+
ACCESS_TOKEN_TAG,
|
|
20
|
+
APPLICATION_JSON_HEADER,
|
|
21
|
+
EXTERNAL_USER_ID,
|
|
22
|
+
)
|
|
23
|
+
from knowledge.services.session import TokenManager, RefreshableSession, TimedSession, PermanentSession
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class WacomServiceException(Exception):
|
|
27
|
+
"""Exception thrown if Wacom service fails.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
message: str
|
|
32
|
+
Error message
|
|
33
|
+
payload: Optional[Dict[str, Any]] (Default:= None)
|
|
34
|
+
Payload
|
|
35
|
+
params: Optional[Dict[str, Any]] (Default:= None)
|
|
36
|
+
Parameters
|
|
37
|
+
method: Optional[str] (Default:= None)
|
|
38
|
+
Method
|
|
39
|
+
url: Optional[str] (Default:= None)
|
|
40
|
+
URL
|
|
41
|
+
service_response: Optional[str] (Default:= None)
|
|
42
|
+
Service response
|
|
43
|
+
status_code: int (Default:= 500)
|
|
44
|
+
Status code
|
|
45
|
+
|
|
46
|
+
Attributes
|
|
47
|
+
----------
|
|
48
|
+
headers: Optional[Dict[str, Any]]
|
|
49
|
+
Headers of the exception
|
|
50
|
+
method: Optional[str]
|
|
51
|
+
Method of the exception
|
|
52
|
+
params: Optional[Dict[str, Any]]
|
|
53
|
+
Parameters of the exception
|
|
54
|
+
payload: Optional[Dict[str, Any]]
|
|
55
|
+
Payload of the exception
|
|
56
|
+
url: Optional[str]
|
|
57
|
+
URL of the exception
|
|
58
|
+
message: str
|
|
59
|
+
Message of the exception
|
|
60
|
+
status_code: int
|
|
61
|
+
Status code of the exception
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
message: str,
|
|
67
|
+
headers: Optional[Dict[str, Any]] = None,
|
|
68
|
+
payload: Optional[Dict[str, Any]] = None,
|
|
69
|
+
params: Optional[Dict[str, Any]] = None,
|
|
70
|
+
method: Optional[str] = None,
|
|
71
|
+
url: Optional[str] = None,
|
|
72
|
+
service_response: Optional[str] = None,
|
|
73
|
+
status_code: int = 500,
|
|
74
|
+
):
|
|
75
|
+
super().__init__(message)
|
|
76
|
+
self.__status_code: int = status_code
|
|
77
|
+
self.__service_response: Optional[str] = service_response
|
|
78
|
+
self.__message: str = message
|
|
79
|
+
self.__headers: Optional[Dict[str, Any]] = headers
|
|
80
|
+
self.__payload: Optional[Dict[str, Any]] = payload
|
|
81
|
+
self.__params: Optional[Dict[str, Any]] = params
|
|
82
|
+
self.__method: Optional[str] = method
|
|
83
|
+
self.__url: Optional[str] = url
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def headers(self) -> Optional[Dict[str, Any]]:
|
|
87
|
+
"""Headers of the exception."""
|
|
88
|
+
return self.__headers
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def method(self) -> Optional[str]:
|
|
92
|
+
"""Method of the exception."""
|
|
93
|
+
return self.__method
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def params(self) -> Optional[Dict[str, Any]]:
|
|
97
|
+
"""Parameters of the exception."""
|
|
98
|
+
return self.__params
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def payload(self) -> Optional[Dict[str, Any]]:
|
|
102
|
+
"""Payload of the exception."""
|
|
103
|
+
return self.__payload
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def url(self) -> Optional[str]:
|
|
107
|
+
"""URL of the exception."""
|
|
108
|
+
return self.__url
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def message(self) -> str:
|
|
112
|
+
"""Message of the exception."""
|
|
113
|
+
return self.__message
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def service_response(self) -> Optional[Response]:
|
|
117
|
+
"""Service response."""
|
|
118
|
+
return self.__service_response
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def status_code(self) -> int:
|
|
122
|
+
"""Status code of the exception."""
|
|
123
|
+
return self.__status_code
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def format_exception(exception: WacomServiceException) -> str:
|
|
127
|
+
"""
|
|
128
|
+
Formats the exception.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
exception: WacomServiceException
|
|
133
|
+
Exception
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
formatted_exception: str
|
|
138
|
+
Formatted exception
|
|
139
|
+
"""
|
|
140
|
+
return (
|
|
141
|
+
f"WacomServiceException: {exception.message}\n"
|
|
142
|
+
"--------------------------------------------------\n"
|
|
143
|
+
f"URL:= {exception.url}\n,"
|
|
144
|
+
f"method:= {exception.method}\n,"
|
|
145
|
+
f"parameters:= {exception.params}\n,"
|
|
146
|
+
f"payload:= {exception.payload}\n,"
|
|
147
|
+
f"headers:= {exception.headers}\n,"
|
|
148
|
+
f"status code=: {exception.status_code}\n,"
|
|
149
|
+
f"service response:= {exception.service_response}"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def handle_error(
|
|
154
|
+
message: str,
|
|
155
|
+
response: Response,
|
|
156
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
157
|
+
payload: Optional[Dict[str, Any]] = None,
|
|
158
|
+
headers: Optional[Dict[str, str]] = None,
|
|
159
|
+
) -> WacomServiceException:
|
|
160
|
+
"""
|
|
161
|
+
Handles an error response.
|
|
162
|
+
|
|
163
|
+
Parameters
|
|
164
|
+
----------
|
|
165
|
+
message: str
|
|
166
|
+
Error message
|
|
167
|
+
response: aiohttp.ClientResponse
|
|
168
|
+
Response
|
|
169
|
+
parameters: Optional[Dict[str, Any]] (Default:= None)
|
|
170
|
+
Parameters
|
|
171
|
+
payload: Optional[Dict[str, Any]] (Default:= None)
|
|
172
|
+
Payload
|
|
173
|
+
headers: Optional[Dict[str, str]] (Default:= None)
|
|
174
|
+
Headers
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
WacomServiceException
|
|
179
|
+
Returns the generated exception.
|
|
180
|
+
"""
|
|
181
|
+
return WacomServiceException(
|
|
182
|
+
message,
|
|
183
|
+
url=response.url,
|
|
184
|
+
method=response.request.method,
|
|
185
|
+
params=parameters,
|
|
186
|
+
payload=payload,
|
|
187
|
+
headers=headers,
|
|
188
|
+
status_code=response.status_code,
|
|
189
|
+
service_response=response.text,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class RESTAPIClient(ABC):
|
|
194
|
+
"""
|
|
195
|
+
Abstract REST API client
|
|
196
|
+
------------------------
|
|
197
|
+
REST API client handling the service url.
|
|
198
|
+
|
|
199
|
+
Arguments
|
|
200
|
+
---------
|
|
201
|
+
service_url: str
|
|
202
|
+
Service URL for service
|
|
203
|
+
verify_calls: bool (default:= False)
|
|
204
|
+
Flag if the service calls should be verified
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
def __init__(self, service_url: str, verify_calls: bool = False):
|
|
208
|
+
self.__service_url: str = service_url.rstrip("/")
|
|
209
|
+
self.__verify_calls: bool = verify_calls
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def service_url(self) -> str:
|
|
213
|
+
"""Service URL."""
|
|
214
|
+
return self.__service_url
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def verify_calls(self):
|
|
218
|
+
"""Certificate verification activated."""
|
|
219
|
+
return self.__verify_calls
|
|
220
|
+
|
|
221
|
+
@verify_calls.setter
|
|
222
|
+
def verify_calls(self, value: bool):
|
|
223
|
+
self.__verify_calls = value
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class WacomServiceAPIClient(RESTAPIClient):
|
|
227
|
+
"""
|
|
228
|
+
Wacom Service API Client
|
|
229
|
+
------------------------
|
|
230
|
+
Abstract class for Wacom service APIs.
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
application_name: str
|
|
235
|
+
Name of the application using the service
|
|
236
|
+
service_url: str
|
|
237
|
+
URL of the service
|
|
238
|
+
service_endpoint: str
|
|
239
|
+
Base endpoint
|
|
240
|
+
auth_service_endpoint: str (Default:= 'graph/v1')
|
|
241
|
+
Authentication service endpoint
|
|
242
|
+
verify_calls: bool (Default:= True)
|
|
243
|
+
Flag if API calls should be verified.
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
USER_ENDPOINT: str = "user"
|
|
247
|
+
USER_LOGIN_ENDPOINT: str = f"{USER_ENDPOINT}/login"
|
|
248
|
+
USER_REFRESH_ENDPOINT: str = f"{USER_ENDPOINT}/refresh"
|
|
249
|
+
SERVICE_URL: str = "https://private-knowledge.wacom.com"
|
|
250
|
+
"""Production service URL"""
|
|
251
|
+
STAGING_SERVICE_URL: str = "https://stage-private-knowledge.wacom.com"
|
|
252
|
+
"""Staging service URL"""
|
|
253
|
+
|
|
254
|
+
def __init__(
|
|
255
|
+
self,
|
|
256
|
+
application_name: str,
|
|
257
|
+
service_url: str,
|
|
258
|
+
service_endpoint: str,
|
|
259
|
+
auth_service_endpoint: str = "graph/v1",
|
|
260
|
+
verify_calls: bool = True,
|
|
261
|
+
):
|
|
262
|
+
self.__application_name: str = application_name
|
|
263
|
+
self.__service_endpoint: str = service_endpoint
|
|
264
|
+
self.__auth_service_endpoint: str = auth_service_endpoint
|
|
265
|
+
self.__token_manager: TokenManager = TokenManager()
|
|
266
|
+
self.__current_session_id: Optional[str] = None
|
|
267
|
+
super().__init__(service_url, verify_calls)
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def token_manager(self) -> TokenManager:
|
|
271
|
+
"""Token manager."""
|
|
272
|
+
return self.__token_manager
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def auth_endpoint(self) -> str:
|
|
276
|
+
"""Authentication endpoint."""
|
|
277
|
+
# This is in graph service REST API
|
|
278
|
+
return f"{self.service_url}/{self.__auth_service_endpoint}/{self.USER_LOGIN_ENDPOINT}"
|
|
279
|
+
|
|
280
|
+
@property
|
|
281
|
+
def current_session(self) -> Union[RefreshableSession, TimedSession, PermanentSession, None]:
|
|
282
|
+
"""Current session.
|
|
283
|
+
|
|
284
|
+
Returns
|
|
285
|
+
-------
|
|
286
|
+
session: Union[TimedSession, RefreshableSession, PermanentSession]
|
|
287
|
+
Current session
|
|
288
|
+
|
|
289
|
+
Raises
|
|
290
|
+
------
|
|
291
|
+
WacomServiceException
|
|
292
|
+
Exception if no session is available.
|
|
293
|
+
"""
|
|
294
|
+
if self.__current_session_id is None:
|
|
295
|
+
raise WacomServiceException("No session set. Please login first.")
|
|
296
|
+
session: Union[RefreshableSession, TimedSession, PermanentSession, None] = self.__token_manager.get_session(
|
|
297
|
+
self.__current_session_id
|
|
298
|
+
)
|
|
299
|
+
if session is None:
|
|
300
|
+
raise WacomServiceException(f"Unknown session id:= {self.__current_session_id}. Please login first.")
|
|
301
|
+
return session
|
|
302
|
+
|
|
303
|
+
def request_user_token(self, tenant_api_key: str, external_id: str) -> Tuple[str, str, datetime]:
|
|
304
|
+
"""
|
|
305
|
+
Login as user by using the tenant key and its external user id.
|
|
306
|
+
|
|
307
|
+
Parameters
|
|
308
|
+
----------
|
|
309
|
+
tenant_api_key: str
|
|
310
|
+
Tenant API key
|
|
311
|
+
external_id: str
|
|
312
|
+
External id.
|
|
313
|
+
|
|
314
|
+
Returns
|
|
315
|
+
-------
|
|
316
|
+
auth_key: str
|
|
317
|
+
Authentication key for identifying the user for the service calls.
|
|
318
|
+
refresh_key: str
|
|
319
|
+
Refresh token
|
|
320
|
+
expiration_time: datatime
|
|
321
|
+
Expiration time
|
|
322
|
+
|
|
323
|
+
Raises
|
|
324
|
+
------
|
|
325
|
+
WacomServiceException
|
|
326
|
+
Exception if service returns HTTP error code.
|
|
327
|
+
"""
|
|
328
|
+
url: str = f"{self.auth_endpoint}"
|
|
329
|
+
headers: dict = {
|
|
330
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
331
|
+
TENANT_API_KEY: tenant_api_key,
|
|
332
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
333
|
+
}
|
|
334
|
+
payload: dict = {EXTERNAL_USER_ID: external_id}
|
|
335
|
+
response: Response = requests.post(
|
|
336
|
+
url, headers=headers, json=payload, timeout=DEFAULT_TIMEOUT, verify=self.verify_calls
|
|
337
|
+
)
|
|
338
|
+
if response.ok:
|
|
339
|
+
try:
|
|
340
|
+
response_token: Dict[str, str] = response.json()
|
|
341
|
+
timestamp_str_truncated: str = ""
|
|
342
|
+
try:
|
|
343
|
+
if sys.version_info <= (3, 10):
|
|
344
|
+
timestamp_str_truncated = response_token[EXPIRATION_DATE_TAG][:19] + "+00:00"
|
|
345
|
+
else:
|
|
346
|
+
timestamp_str_truncated = response_token[EXPIRATION_DATE_TAG]
|
|
347
|
+
date_object: datetime = datetime.fromisoformat(timestamp_str_truncated)
|
|
348
|
+
except (TypeError, ValueError) as _:
|
|
349
|
+
date_object: datetime = datetime.now()
|
|
350
|
+
logger.warning(
|
|
351
|
+
f"Parsing of expiration date failed. {response_token[EXPIRATION_DATE_TAG]} "
|
|
352
|
+
f"-> {timestamp_str_truncated}"
|
|
353
|
+
)
|
|
354
|
+
return response_token["accessToken"], response_token["refreshToken"], date_object
|
|
355
|
+
except Exception as e:
|
|
356
|
+
raise handle_error(f"Parsing of response failed. {e}", response) from e
|
|
357
|
+
raise handle_error("User login failed.", response)
|
|
358
|
+
|
|
359
|
+
def login(self, tenant_api_key: str, external_user_id: str) -> PermanentSession:
|
|
360
|
+
"""Login as user by using the tenant id and its external user id.
|
|
361
|
+
Parameters
|
|
362
|
+
----------
|
|
363
|
+
tenant_api_key: str
|
|
364
|
+
Tenant id
|
|
365
|
+
external_user_id: str
|
|
366
|
+
External user id
|
|
367
|
+
Returns
|
|
368
|
+
-------
|
|
369
|
+
session: PermanentSession
|
|
370
|
+
Session. The session is stored in the token manager and the client is using the session id for further
|
|
371
|
+
calls.
|
|
372
|
+
"""
|
|
373
|
+
auth_key, refresh_token, _ = self.request_user_token(tenant_api_key, external_user_id)
|
|
374
|
+
session: PermanentSession = self.__token_manager.add_session(
|
|
375
|
+
auth_token=auth_key,
|
|
376
|
+
refresh_token=refresh_token,
|
|
377
|
+
tenant_api_key=tenant_api_key,
|
|
378
|
+
external_user_id=external_user_id,
|
|
379
|
+
)
|
|
380
|
+
self.__current_session_id = session.id
|
|
381
|
+
return session
|
|
382
|
+
|
|
383
|
+
def logout(self):
|
|
384
|
+
"""Logout user."""
|
|
385
|
+
self.__current_session_id = None
|
|
386
|
+
|
|
387
|
+
def register_token(
|
|
388
|
+
self, auth_key: str, refresh_token: Optional[str] = None
|
|
389
|
+
) -> Union[RefreshableSession, TimedSession]:
|
|
390
|
+
"""Register token.
|
|
391
|
+
Parameters
|
|
392
|
+
----------
|
|
393
|
+
auth_key: str
|
|
394
|
+
Authentication key for identifying the user for the service calls.
|
|
395
|
+
refresh_token: str
|
|
396
|
+
Refresh token
|
|
397
|
+
|
|
398
|
+
Returns
|
|
399
|
+
-------
|
|
400
|
+
session: Union[RefreshableSession, TimedSession]
|
|
401
|
+
Session. The session is stored in the token manager and the client is using the session id for further
|
|
402
|
+
calls.
|
|
403
|
+
"""
|
|
404
|
+
session = self.__token_manager.add_session(auth_token=auth_key, refresh_token=refresh_token)
|
|
405
|
+
self.__current_session_id = session.id
|
|
406
|
+
if isinstance(session, (RefreshableSession, TimedSession)):
|
|
407
|
+
return session
|
|
408
|
+
raise WacomServiceException(f"Wrong session type:= {type(session)}.")
|
|
409
|
+
|
|
410
|
+
def use_session(self, session_id: str):
|
|
411
|
+
"""Use session.
|
|
412
|
+
Parameters
|
|
413
|
+
----------
|
|
414
|
+
session_id: str
|
|
415
|
+
Session id
|
|
416
|
+
"""
|
|
417
|
+
if self.__token_manager.has_session(session_id):
|
|
418
|
+
self.__current_session_id = session_id
|
|
419
|
+
else:
|
|
420
|
+
raise WacomServiceException(f"Unknown session id:= {session_id}.")
|
|
421
|
+
|
|
422
|
+
def refresh_token(self, refresh_token: str) -> Tuple[str, str, datetime]:
|
|
423
|
+
"""
|
|
424
|
+
Refreshing a token.
|
|
425
|
+
|
|
426
|
+
Parameters
|
|
427
|
+
----------
|
|
428
|
+
refresh_token: str
|
|
429
|
+
Refresh token
|
|
430
|
+
|
|
431
|
+
Returns
|
|
432
|
+
-------
|
|
433
|
+
auth_key: str
|
|
434
|
+
Authentication key for identifying the user for the service calls.
|
|
435
|
+
refresh_key: str
|
|
436
|
+
Refresh token
|
|
437
|
+
expiration_time: str
|
|
438
|
+
Expiration time
|
|
439
|
+
|
|
440
|
+
Raises
|
|
441
|
+
------
|
|
442
|
+
WacomServiceException
|
|
443
|
+
Exception if service returns HTTP error code.
|
|
444
|
+
"""
|
|
445
|
+
url: str = f"{self.service_base_url}{WacomServiceAPIClient.USER_REFRESH_ENDPOINT}/"
|
|
446
|
+
headers: Dict[str, str] = {
|
|
447
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
448
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
449
|
+
}
|
|
450
|
+
payload: Dict[str, str] = {REFRESH_TOKEN_TAG: refresh_token}
|
|
451
|
+
response: Response = requests.post(
|
|
452
|
+
url, headers=headers, json=payload, timeout=DEFAULT_TIMEOUT, verify=self.verify_calls
|
|
453
|
+
)
|
|
454
|
+
if response.ok:
|
|
455
|
+
response_token: Dict[str, str] = response.json()
|
|
456
|
+
try:
|
|
457
|
+
date_object: datetime = datetime.fromisoformat(response_token[EXPIRATION_DATE_TAG])
|
|
458
|
+
except (TypeError, ValueError) as _:
|
|
459
|
+
date_object: datetime = datetime.now()
|
|
460
|
+
logger.warning(f"Parsing of expiration date failed. {response_token[EXPIRATION_DATE_TAG]}")
|
|
461
|
+
return response_token[ACCESS_TOKEN_TAG], response_token[REFRESH_TOKEN_TAG], date_object
|
|
462
|
+
raise handle_error("Refreshing token failed.", response)
|
|
463
|
+
|
|
464
|
+
def handle_token(self, force_refresh: bool = False, force_refresh_timeout: float = 120) -> Tuple[str, str]:
|
|
465
|
+
"""
|
|
466
|
+
Handles the token and refreshes it if needed.
|
|
467
|
+
|
|
468
|
+
Parameters
|
|
469
|
+
----------
|
|
470
|
+
force_refresh: bool
|
|
471
|
+
Force refresh token
|
|
472
|
+
force_refresh_timeout: int
|
|
473
|
+
Force refresh timeout
|
|
474
|
+
Returns
|
|
475
|
+
-------
|
|
476
|
+
user_token: str
|
|
477
|
+
The user token
|
|
478
|
+
refresh_token: str
|
|
479
|
+
The refresh token
|
|
480
|
+
"""
|
|
481
|
+
# The session is not set
|
|
482
|
+
if self.current_session is None:
|
|
483
|
+
raise WacomServiceException("Authentication key is not set. Please login first.")
|
|
484
|
+
|
|
485
|
+
# The token expired and is not refreshable
|
|
486
|
+
if not self.current_session.refreshable and self.current_session.expired:
|
|
487
|
+
raise WacomServiceException("Authentication key is expired and cannot be refreshed. Please login again.")
|
|
488
|
+
|
|
489
|
+
# The token is not refreshable and the force refresh flag is set
|
|
490
|
+
if not self.current_session.refreshable and force_refresh:
|
|
491
|
+
raise WacomServiceException("Authentication key is not refreshable. Please login again.")
|
|
492
|
+
|
|
493
|
+
# Refresh token if needed
|
|
494
|
+
if self.current_session.refreshable and (
|
|
495
|
+
self.current_session.expires_in < force_refresh_timeout or force_refresh
|
|
496
|
+
):
|
|
497
|
+
try:
|
|
498
|
+
auth_key, refresh_token, _ = self.refresh_token(self.current_session.refresh_token)
|
|
499
|
+
except WacomServiceException as e:
|
|
500
|
+
if isinstance(self.current_session, PermanentSession):
|
|
501
|
+
permanent_session: PermanentSession = self.current_session
|
|
502
|
+
auth_key, refresh_token, _ = self.request_user_token(
|
|
503
|
+
permanent_session.tenant_api_key, permanent_session.external_user_id
|
|
504
|
+
)
|
|
505
|
+
else:
|
|
506
|
+
logger.error(f"Error refreshing token: {e}")
|
|
507
|
+
raise e
|
|
508
|
+
self.current_session.update_session(auth_key, refresh_token)
|
|
509
|
+
return auth_key, refresh_token
|
|
510
|
+
return self.current_session.auth_token, self.current_session.refresh_token
|
|
511
|
+
|
|
512
|
+
@property
|
|
513
|
+
def user_agent(self) -> str:
|
|
514
|
+
"""User agent."""
|
|
515
|
+
return (
|
|
516
|
+
f"Personal Knowledge Library({self.application_name})/{__version__}"
|
|
517
|
+
f"(+https://github.com/Wacom-Developer/personal-knowledge-library)"
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
@property
|
|
521
|
+
def service_endpoint(self):
|
|
522
|
+
"""Service endpoint."""
|
|
523
|
+
return "" if len(self.__service_endpoint) == 0 else f"{self.__service_endpoint}/"
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def service_base_url(self):
|
|
527
|
+
"""Service endpoint."""
|
|
528
|
+
return f"{self.service_url}/{self.service_endpoint}"
|
|
529
|
+
|
|
530
|
+
@property
|
|
531
|
+
def application_name(self):
|
|
532
|
+
"""Application name."""
|
|
533
|
+
return self.__application_name
|