wcp-library 1.2.9__py3-none-any.whl → 1.3.1__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.
- wcp_library/{async_credentials/oracle.py → credentials/credential_manager_asynchronous.py} +46 -37
- wcp_library/credentials/credential_manager_synchronous.py +144 -0
- wcp_library/credentials/ftp.py +27 -79
- wcp_library/credentials/oracle.py +30 -78
- wcp_library/credentials/postgres.py +27 -79
- wcp_library/sql/__init__.py +28 -1
- wcp_library/sql/oracle.py +255 -2
- wcp_library/sql/postgres.py +242 -2
- {wcp_library-1.2.9.dist-info → wcp_library-1.3.1.dist-info}/METADATA +1 -1
- wcp_library-1.3.1.dist-info/RECORD +20 -0
- wcp_library/async_credentials/__init__.py +0 -49
- wcp_library/async_credentials/api.py +0 -0
- wcp_library/async_credentials/postgres.py +0 -130
- wcp_library/async_sql/__init__.py +0 -35
- wcp_library/async_sql/oracle.py +0 -261
- wcp_library/async_sql/postgres.py +0 -252
- wcp_library-1.2.9.dist-info/RECORD +0 -25
- {wcp_library-1.2.9.dist-info → wcp_library-1.3.1.dist-info}/WHEEL +0 -0
wcp_library/sql/postgres.py
CHANGED
@@ -4,9 +4,9 @@ from typing import Optional
|
|
4
4
|
import numpy as np
|
5
5
|
import pandas as pd
|
6
6
|
from psycopg.sql import SQL
|
7
|
-
from psycopg_pool import ConnectionPool
|
7
|
+
from psycopg_pool import AsyncConnectionPool, ConnectionPool
|
8
8
|
|
9
|
-
from wcp_library.sql import retry
|
9
|
+
from wcp_library.sql import retry, async_retry
|
10
10
|
|
11
11
|
logger = logging.getLogger(__name__)
|
12
12
|
|
@@ -37,6 +37,35 @@ def _connect_warehouse(username: str, password: str, hostname: str, port: int, d
|
|
37
37
|
return session_pool
|
38
38
|
|
39
39
|
|
40
|
+
async def _async_connect_warehouse(username: str, password: str, hostname: str, port: int, database: str, min_connections: int,
|
41
|
+
max_connections: int) -> AsyncConnectionPool:
|
42
|
+
"""
|
43
|
+
Create Warehouse Connection
|
44
|
+
|
45
|
+
:param username: username
|
46
|
+
:param password: password
|
47
|
+
:param hostname: hostname
|
48
|
+
:param port: port
|
49
|
+
:param database: database
|
50
|
+
:param min_connections:
|
51
|
+
:param max_connections:
|
52
|
+
:return: session_pool
|
53
|
+
"""
|
54
|
+
|
55
|
+
url = f"postgres://{username}:{password}@{hostname}:{port}/{database}"
|
56
|
+
|
57
|
+
session_pool = AsyncConnectionPool(
|
58
|
+
conninfo=url,
|
59
|
+
min_size=min_connections,
|
60
|
+
max_size=max_connections,
|
61
|
+
)
|
62
|
+
await session_pool.open()
|
63
|
+
return session_pool
|
64
|
+
|
65
|
+
|
66
|
+
"""~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
|
67
|
+
|
68
|
+
|
40
69
|
class PostgresConnection(object):
|
41
70
|
"""
|
42
71
|
SQL Connection Class
|
@@ -252,3 +281,214 @@ class PostgresConnection(object):
|
|
252
281
|
"""
|
253
282
|
|
254
283
|
self._session_pool.close()
|
284
|
+
|
285
|
+
|
286
|
+
class AsyncPostgresConnection(object):
|
287
|
+
"""
|
288
|
+
SQL Connection Class
|
289
|
+
|
290
|
+
:return: None
|
291
|
+
"""
|
292
|
+
|
293
|
+
def __init__(self, min_connections: int = 2, max_connections: int = 5):
|
294
|
+
self._username: Optional[str] = None
|
295
|
+
self._password: Optional[str] = None
|
296
|
+
self._hostname: Optional[str] = None
|
297
|
+
self._port: Optional[int] = None
|
298
|
+
self._database: Optional[str] = None
|
299
|
+
self._session_pool: Optional[AsyncConnectionPool] = None
|
300
|
+
|
301
|
+
self.min_connections = min_connections
|
302
|
+
self.max_connections = max_connections
|
303
|
+
|
304
|
+
self._retry_count = 0
|
305
|
+
self.retry_limit = 50
|
306
|
+
self.retry_error_codes = ['08001', '08004']
|
307
|
+
|
308
|
+
@async_retry
|
309
|
+
async def _connect(self) -> None:
|
310
|
+
"""
|
311
|
+
Connect to the warehouse
|
312
|
+
|
313
|
+
:return: None
|
314
|
+
"""
|
315
|
+
|
316
|
+
self._session_pool = await _connect_warehouse(self._username, self._password, self._hostname, self._port,
|
317
|
+
self._database, self.min_connections, self.max_connections)
|
318
|
+
|
319
|
+
async def set_user(self, credentials_dict: dict) -> None:
|
320
|
+
"""
|
321
|
+
Set the user credentials and connect
|
322
|
+
|
323
|
+
:param credentials_dict: dictionary of connection details
|
324
|
+
:return: None
|
325
|
+
"""
|
326
|
+
|
327
|
+
self._username: Optional[str] = credentials_dict['UserName']
|
328
|
+
self._password: Optional[str] = credentials_dict['Password']
|
329
|
+
self._hostname: Optional[str] = credentials_dict['Host']
|
330
|
+
self._port: Optional[int] = int(credentials_dict['Port'])
|
331
|
+
self._database: Optional[str] = credentials_dict['Database']
|
332
|
+
|
333
|
+
await self._connect()
|
334
|
+
|
335
|
+
async def close_connection(self) -> None:
|
336
|
+
"""
|
337
|
+
Close the connection
|
338
|
+
|
339
|
+
:return: None
|
340
|
+
"""
|
341
|
+
|
342
|
+
await self._session_pool.close()
|
343
|
+
|
344
|
+
@async_retry
|
345
|
+
async def execute(self, query: SQL | str) -> None:
|
346
|
+
"""
|
347
|
+
Execute the query
|
348
|
+
|
349
|
+
:param query: query
|
350
|
+
:return: None
|
351
|
+
"""
|
352
|
+
|
353
|
+
async with self._session_pool.connection() as connection:
|
354
|
+
await connection.execute(query)
|
355
|
+
|
356
|
+
@async_retry
|
357
|
+
async def safe_execute(self, query: SQL | str, packed_values: dict) -> None:
|
358
|
+
"""
|
359
|
+
Execute the query without SQL Injection possibility, to be used with external facing projects.
|
360
|
+
|
361
|
+
:param query: query
|
362
|
+
:param packed_values: dictionary of values
|
363
|
+
:return: None
|
364
|
+
"""
|
365
|
+
|
366
|
+
async with self._session_pool.connection() as connection:
|
367
|
+
await connection.execute(query, packed_values)
|
368
|
+
|
369
|
+
@async_retry
|
370
|
+
async def execute_multiple(self, queries: list[list[SQL | str, dict]]) -> None:
|
371
|
+
"""
|
372
|
+
Execute multiple queries
|
373
|
+
|
374
|
+
:param queries: list of queries
|
375
|
+
:return: None
|
376
|
+
"""
|
377
|
+
|
378
|
+
async with self._session_pool.connection() as connection:
|
379
|
+
for item in queries:
|
380
|
+
query = item[0]
|
381
|
+
packed_values = item[1]
|
382
|
+
if packed_values:
|
383
|
+
await connection.execute(query, packed_values)
|
384
|
+
else:
|
385
|
+
await connection.execute(query)
|
386
|
+
|
387
|
+
@async_retry
|
388
|
+
async def execute_many(self, query: SQL | str, dictionary: list[dict]) -> None:
|
389
|
+
"""
|
390
|
+
Execute many queries
|
391
|
+
|
392
|
+
:param query: query
|
393
|
+
:param dictionary: dictionary of values
|
394
|
+
:return: None
|
395
|
+
"""
|
396
|
+
|
397
|
+
async with self._session_pool.connection() as connection:
|
398
|
+
await connection.executemany(query, dictionary)
|
399
|
+
|
400
|
+
@async_retry
|
401
|
+
async def fetch_data(self, query: SQL | str, packed_data=None):
|
402
|
+
"""
|
403
|
+
Fetch the data from the query
|
404
|
+
|
405
|
+
:param query: query
|
406
|
+
:param packed_data: packed data
|
407
|
+
:return: rows
|
408
|
+
"""
|
409
|
+
|
410
|
+
async with self._session_pool.connection() as connection:
|
411
|
+
cursor = connection.cursor()
|
412
|
+
if packed_data:
|
413
|
+
await cursor.execute(query, packed_data)
|
414
|
+
else:
|
415
|
+
await cursor.execute(query)
|
416
|
+
rows = await cursor.fetchall()
|
417
|
+
return rows
|
418
|
+
|
419
|
+
@async_retry
|
420
|
+
async def remove_matching_data(self, dfObj: pd.DataFrame, outputTableName: str, match_cols: list) -> None:
|
421
|
+
"""
|
422
|
+
Remove matching data from the warehouse
|
423
|
+
|
424
|
+
:param dfObj: DataFrame
|
425
|
+
:param outputTableName: output table name
|
426
|
+
:param match_cols: list of columns
|
427
|
+
:return: None
|
428
|
+
"""
|
429
|
+
|
430
|
+
df = dfObj[match_cols]
|
431
|
+
param_list = []
|
432
|
+
for column in match_cols:
|
433
|
+
param_list.append(f"{column} = %({column})s")
|
434
|
+
if len(param_list) > 1:
|
435
|
+
params = ' AND '.join(param_list)
|
436
|
+
else:
|
437
|
+
params = param_list[0]
|
438
|
+
|
439
|
+
main_dict = df.to_dict('records')
|
440
|
+
query = """DELETE FROM {} WHERE {}""".format(outputTableName, params)
|
441
|
+
await self.execute_many(query, main_dict)
|
442
|
+
|
443
|
+
@async_retry
|
444
|
+
async def export_DF_to_warehouse(self, dfObj: pd.DataFrame, outputTableName: str, columns: list, remove_nan=False) -> None:
|
445
|
+
"""
|
446
|
+
Export the DataFrame to the warehouse
|
447
|
+
|
448
|
+
:param dfObj: DataFrame
|
449
|
+
:param outputTableName: output table name
|
450
|
+
:param columns: list of columns
|
451
|
+
:param remove_nan: remove NaN values
|
452
|
+
:return: None
|
453
|
+
"""
|
454
|
+
|
455
|
+
col = ', '.join(columns)
|
456
|
+
param_list = []
|
457
|
+
for column in columns:
|
458
|
+
param_list.append(f"%({column})s")
|
459
|
+
params = ', '.join(param_list)
|
460
|
+
|
461
|
+
if remove_nan:
|
462
|
+
dfObj = dfObj.replace({np.nan: None})
|
463
|
+
main_dict = dfObj.to_dict('records')
|
464
|
+
for record in main_dict:
|
465
|
+
for key in record:
|
466
|
+
if record[key] == '':
|
467
|
+
record[key] = None
|
468
|
+
|
469
|
+
query = """INSERT INTO {} ({}) VALUES ({})""".format(outputTableName, col, params)
|
470
|
+
await self.execute_many(query, main_dict)
|
471
|
+
|
472
|
+
@async_retry
|
473
|
+
async def truncate_table(self, tableName: str) -> None:
|
474
|
+
"""
|
475
|
+
Truncate the table
|
476
|
+
|
477
|
+
:param tableName: table name
|
478
|
+
:return: None
|
479
|
+
"""
|
480
|
+
|
481
|
+
truncateQuery = """TRUNCATE TABLE {}""".format(tableName)
|
482
|
+
await self.execute(truncateQuery)
|
483
|
+
|
484
|
+
@async_retry
|
485
|
+
async def empty_table(self, tableName: str) -> None:
|
486
|
+
"""
|
487
|
+
Empty the table
|
488
|
+
|
489
|
+
:param tableName: table name
|
490
|
+
:return: None
|
491
|
+
"""
|
492
|
+
|
493
|
+
deleteQuery = """DELETE FROM {}""".format(tableName)
|
494
|
+
await self.execute(deleteQuery)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
wcp_library/__init__.py,sha256=hwLbcu00uI6L_xjXO9-I0YcODl2WtIOkdNLoDcXv7zk,591
|
2
|
+
wcp_library/credentials/__init__.py,sha256=HRmg7mqcATeclIz3lZQjSR4nmK6aY6MK9-QXEYZoFrw,1857
|
3
|
+
wcp_library/credentials/credential_manager_asynchronous.py,sha256=oTuo-TJpOWU52eaW6fhQL6QZ3zZvz_1WSpuSeA9thco,5728
|
4
|
+
wcp_library/credentials/credential_manager_synchronous.py,sha256=ouPNLt20FvocuMoFx49mbDL7-Moj_WrlpR7k97mx-i4,5512
|
5
|
+
wcp_library/credentials/ftp.py,sha256=O4oSPtCCv_0w6sLITFnY9EpN6-K2XxeibbGB0VnTHJ4,2589
|
6
|
+
wcp_library/credentials/oracle.py,sha256=m0WtmSyUdKUfsz1SPkRgc7A080rK6cq7jVoQ0YcWJ50,2867
|
7
|
+
wcp_library/credentials/postgres.py,sha256=tCCWdc10lgdu6zbU-Hv0ZxGw9rn6ZvDvFkMZqm9qfBo,2571
|
8
|
+
wcp_library/emailing.py,sha256=xqNly6Tmj-pvwl5bdys3gauZFDd4SuWCQYzGFNemv2Q,2496
|
9
|
+
wcp_library/ftp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
wcp_library/ftp/ftp.py,sha256=EpyW0J2QIGxP8zVGD4VarA0hi4C2XAPDPF-0j2sRdpI,4350
|
11
|
+
wcp_library/ftp/sftp.py,sha256=hykXGLGdxe7DYAxFdTwjPjTEOYuIpSMyK3NOiTQNUK0,4176
|
12
|
+
wcp_library/informatica.py,sha256=IXZtk_9X1XLbOEwFrsyOwTgajQKvtXgANBHmuTOP3Kk,4064
|
13
|
+
wcp_library/logging.py,sha256=e6gG7HFgUrMajUZs4Gs0atFfOJJmdmxN0GerfynNWlY,2061
|
14
|
+
wcp_library/selenium_helper.py,sha256=rlphTXsUgnbaXZknY5nfQqxFhnc7UmrpzhV3hQ-cv7k,2509
|
15
|
+
wcp_library/sql/__init__.py,sha256=s2psmwkq_ZU23iGWvXjJrLu0hD1fB6CDv6RHcK7y828,1917
|
16
|
+
wcp_library/sql/oracle.py,sha256=TGiTC5L5UcM5QcHFajgn43NI8HygOGIEAtLmLbVFp2I,15772
|
17
|
+
wcp_library/sql/postgres.py,sha256=ybk7WudcuT3_SmdkWrl01Z8rmiishHqvB1SCI9nng7g,14392
|
18
|
+
wcp_library-1.3.1.dist-info/METADATA,sha256=jBdqrTJokOY93ioq_ZLIfykk6_V6Mdvx0qFQAaWxaWE,1513
|
19
|
+
wcp_library-1.3.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
20
|
+
wcp_library-1.3.1.dist-info/RECORD,,
|
@@ -1,49 +0,0 @@
|
|
1
|
-
import secrets
|
2
|
-
import string
|
3
|
-
|
4
|
-
|
5
|
-
class MissingCredentialsError(KeyError):
|
6
|
-
pass
|
7
|
-
|
8
|
-
|
9
|
-
async def generate_password(length: int=12, use_nums: bool=True, use_special: bool=True, special_chars_override: list=None, force_num: bool=True, force_spec: bool=True) -> str:
|
10
|
-
"""
|
11
|
-
Function to generate a random password
|
12
|
-
|
13
|
-
:param length:
|
14
|
-
:param use_nums: Allows the use of numbers
|
15
|
-
:param use_special: Allows the use of special characters
|
16
|
-
:param special_chars_override: List of special characters to use
|
17
|
-
:param force_num: Requires the password to contain at least one number
|
18
|
-
:param force_spec: Requires the password to contain at least one special character
|
19
|
-
:return: Password
|
20
|
-
"""
|
21
|
-
|
22
|
-
letters = string.ascii_letters
|
23
|
-
digits = string.digits
|
24
|
-
if special_chars_override:
|
25
|
-
special_chars = special_chars_override
|
26
|
-
else:
|
27
|
-
special_chars = string.punctuation
|
28
|
-
|
29
|
-
alphabet = letters
|
30
|
-
if use_nums:
|
31
|
-
alphabet += digits
|
32
|
-
if use_special:
|
33
|
-
alphabet += special_chars
|
34
|
-
|
35
|
-
pwd = ''
|
36
|
-
for i in range(length):
|
37
|
-
pwd += ''.join(secrets.choice(alphabet))
|
38
|
-
|
39
|
-
if (use_nums and force_num) and (use_special and force_spec):
|
40
|
-
while pwd[0].isdigit() or not any(char.isdigit() for char in pwd) or not any(char in pwd for char in special_chars):
|
41
|
-
pwd = await generate_password(length, use_nums, use_special, special_chars_override, force_num, force_spec)
|
42
|
-
elif use_nums and force_num:
|
43
|
-
while pwd[0].isdigit() or not any(char.isdigit() for char in pwd):
|
44
|
-
pwd = await generate_password(length, use_nums, use_special, special_chars_override, force_num, force_spec)
|
45
|
-
elif use_special and force_spec:
|
46
|
-
while not any(char in pwd for char in special_chars):
|
47
|
-
pwd = await generate_password(length, use_nums, use_special, special_chars_override, force_num, force_spec)
|
48
|
-
|
49
|
-
return pwd
|
File without changes
|
@@ -1,130 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
|
3
|
-
import aiohttp
|
4
|
-
from yarl import URL
|
5
|
-
|
6
|
-
from wcp_library.async_credentials import MissingCredentialsError
|
7
|
-
|
8
|
-
logger = logging.getLogger(__name__)
|
9
|
-
|
10
|
-
|
11
|
-
class AsyncPostgresCredentialManager:
|
12
|
-
def __init__(self, passwordState_api_key: str):
|
13
|
-
self.password_url = URL("https://vault.wcap.ca/api/passwords/")
|
14
|
-
self.api_key = passwordState_api_key
|
15
|
-
self.headers = {"APIKey": self.api_key, 'Reason': 'Python Script Access'}
|
16
|
-
self._password_list_id = 207
|
17
|
-
|
18
|
-
async def _get_credentials(self) -> dict:
|
19
|
-
"""
|
20
|
-
Get all credentials from the password list
|
21
|
-
|
22
|
-
:return:
|
23
|
-
"""
|
24
|
-
|
25
|
-
logger.debug("Getting credentials from PasswordState")
|
26
|
-
url = (self.password_url / str(self._password_list_id)).with_query("QueryAll")
|
27
|
-
async with aiohttp.ClientSession() as session:
|
28
|
-
async with session.get(str(url), headers=self.headers) as response:
|
29
|
-
passwords = await response.json()
|
30
|
-
|
31
|
-
if not passwords:
|
32
|
-
raise MissingCredentialsError("No credentials found in this Password List")
|
33
|
-
|
34
|
-
password_dict = {}
|
35
|
-
for password in passwords:
|
36
|
-
password_info = {'PasswordID': password['PasswordID'], 'UserName': password['UserName'], 'Password': password['Password']}
|
37
|
-
for field in password['GenericFieldInfo']:
|
38
|
-
password_info[field['DisplayName']] = field['Value'].lower() if field['DisplayName'].lower() == 'username' else field['Value']
|
39
|
-
password_dict[password['UserName'].lower()] = password_info
|
40
|
-
logger.debug("Credentials retrieved")
|
41
|
-
return password_dict
|
42
|
-
|
43
|
-
async def get_credentials(self, username: str) -> dict:
|
44
|
-
"""
|
45
|
-
Get the credentials for a specific username
|
46
|
-
|
47
|
-
:param username:
|
48
|
-
:return:
|
49
|
-
"""
|
50
|
-
|
51
|
-
logger.debug(f"Getting credentials for {username}")
|
52
|
-
credentials = await self._get_credentials()
|
53
|
-
|
54
|
-
try:
|
55
|
-
return_credential = credentials[username.lower()]
|
56
|
-
except KeyError:
|
57
|
-
raise MissingCredentialsError(f"Credentials for {username} not found in this Password List")
|
58
|
-
logger.debug(f"Credentials for {username} retrieved")
|
59
|
-
return return_credential
|
60
|
-
|
61
|
-
async def update_credential(self, credentials_dict: dict) -> bool:
|
62
|
-
"""
|
63
|
-
Update username and password in PasswordState
|
64
|
-
|
65
|
-
Credentials dictionary must have the following keys:
|
66
|
-
- PasswordID
|
67
|
-
- UserName
|
68
|
-
- Password
|
69
|
-
|
70
|
-
The dictionary can be obtained from the get_credentials method
|
71
|
-
|
72
|
-
:param credentials_dict:
|
73
|
-
:return:
|
74
|
-
"""
|
75
|
-
|
76
|
-
logger.debug(f"Updating credentials for {credentials_dict['UserName']}")
|
77
|
-
url = (self.password_url / str(self._password_list_id)).with_query("QueryAll")
|
78
|
-
async with aiohttp.ClientSession() as session:
|
79
|
-
async with session.get(str(url), headers=self.headers) as response:
|
80
|
-
passwords = await response.json()
|
81
|
-
|
82
|
-
relevant_credential_entry = [x for x in passwords if x['UserName'] == credentials_dict['UserName']][0]
|
83
|
-
for field in relevant_credential_entry['GenericFieldInfo']:
|
84
|
-
if field['DisplayName'] in credentials_dict:
|
85
|
-
credentials_dict[field['GenericFieldID']] = credentials_dict[field['DisplayName']]
|
86
|
-
credentials_dict.pop(field['DisplayName'])
|
87
|
-
|
88
|
-
async with aiohttp.ClientSession() as session:
|
89
|
-
async with session.put(str(self.password_url), json=credentials_dict, headers=self.headers) as response:
|
90
|
-
if response.status == 200:
|
91
|
-
logger.debug(f"Credentials for {credentials_dict['UserName']} updated")
|
92
|
-
return True
|
93
|
-
else:
|
94
|
-
logger.error(f"Failed to update credentials for {credentials_dict['UserName']}")
|
95
|
-
return False
|
96
|
-
|
97
|
-
async def new_credentials(self, credentials_dict: dict) -> bool:
|
98
|
-
"""
|
99
|
-
Create a new credential entry
|
100
|
-
|
101
|
-
Credentials dictionary must have the following keys:
|
102
|
-
- UserName
|
103
|
-
- Password
|
104
|
-
- Host
|
105
|
-
- Port
|
106
|
-
- Database
|
107
|
-
|
108
|
-
:param credentials_dict:
|
109
|
-
:return:
|
110
|
-
"""
|
111
|
-
|
112
|
-
data = {
|
113
|
-
"PasswordListID": self._password_list_id,
|
114
|
-
"Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
|
115
|
-
"Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
|
116
|
-
"UserName": credentials_dict['UserName'].lower(),
|
117
|
-
"Password": credentials_dict['Password'],
|
118
|
-
"GenericField1": credentials_dict['Host'],
|
119
|
-
"GenericField2": credentials_dict['Port'],
|
120
|
-
"GenericField3": credentials_dict['Database']
|
121
|
-
}
|
122
|
-
|
123
|
-
async with aiohttp.ClientSession() as session:
|
124
|
-
async with session.post(str(self.password_url), json=data, headers=self.headers) as response:
|
125
|
-
if response.status == 201:
|
126
|
-
logger.debug(f"New credentials for {credentials_dict['UserName']} created")
|
127
|
-
return True
|
128
|
-
else:
|
129
|
-
logger.error(f"Failed to create new credentials for {credentials_dict['UserName']}")
|
130
|
-
return False
|
@@ -1,35 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
import logging
|
3
|
-
from functools import wraps
|
4
|
-
|
5
|
-
import oracledb
|
6
|
-
import psycopg
|
7
|
-
|
8
|
-
logger = logging.getLogger(__name__)
|
9
|
-
|
10
|
-
|
11
|
-
def retry(f: callable) -> callable:
|
12
|
-
"""
|
13
|
-
Decorator to retry a function
|
14
|
-
|
15
|
-
:param f: function
|
16
|
-
:return: function
|
17
|
-
"""
|
18
|
-
|
19
|
-
@wraps(f)
|
20
|
-
async def wrapper(self, *args, **kwargs):
|
21
|
-
self._retry_count = 0
|
22
|
-
while True:
|
23
|
-
try:
|
24
|
-
return await f(self, *args, **kwargs)
|
25
|
-
except (oracledb.OperationalError, psycopg.OperationalError) as e:
|
26
|
-
error_obj, = e.args
|
27
|
-
if error_obj.full_code in self.retry_error_codes and self._retry_count < self.retry_limit:
|
28
|
-
self._retry_count += 1
|
29
|
-
logger.debug(f"{self._db_service} connection error")
|
30
|
-
logger.debug(error_obj.message)
|
31
|
-
logger.info("Waiting 5 minutes before retrying Oracle connection")
|
32
|
-
await asyncio.sleep(300)
|
33
|
-
else:
|
34
|
-
raise e
|
35
|
-
return wrapper
|