wcp-library 1.2.8__tar.gz → 1.3.1__tar.gz

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.
Files changed (30) hide show
  1. {wcp_library-1.2.8 → wcp_library-1.3.1}/PKG-INFO +1 -1
  2. {wcp_library-1.2.8 → wcp_library-1.3.1}/pyproject.toml +1 -1
  3. wcp_library-1.2.8/wcp_library/async_credentials/oracle.py → wcp_library-1.3.1/wcp_library/credentials/credential_manager_asynchronous.py +46 -37
  4. wcp_library-1.2.8/wcp_library/credentials/postgres.py → wcp_library-1.3.1/wcp_library/credentials/credential_manager_synchronous.py +55 -35
  5. wcp_library-1.3.1/wcp_library/credentials/ftp.py +72 -0
  6. wcp_library-1.3.1/wcp_library/credentials/oracle.py +77 -0
  7. wcp_library-1.3.1/wcp_library/credentials/postgres.py +72 -0
  8. {wcp_library-1.2.8/wcp_library/async_sql → wcp_library-1.3.1/wcp_library/sql}/__init__.py +28 -1
  9. wcp_library-1.3.1/wcp_library/sql/oracle.py +527 -0
  10. wcp_library-1.3.1/wcp_library/sql/postgres.py +494 -0
  11. wcp_library-1.2.8/wcp_library/async_credentials/__init__.py +0 -49
  12. wcp_library-1.2.8/wcp_library/async_credentials/api.py +0 -0
  13. wcp_library-1.2.8/wcp_library/async_credentials/postgres.py +0 -130
  14. wcp_library-1.2.8/wcp_library/async_sql/oracle.py +0 -258
  15. wcp_library-1.2.8/wcp_library/async_sql/postgres.py +0 -249
  16. wcp_library-1.2.8/wcp_library/credentials/ftp.py +0 -124
  17. wcp_library-1.2.8/wcp_library/credentials/oracle.py +0 -125
  18. wcp_library-1.2.8/wcp_library/sql/__init__.py +0 -35
  19. wcp_library-1.2.8/wcp_library/sql/oracle.py +0 -271
  20. wcp_library-1.2.8/wcp_library/sql/postgres.py +0 -251
  21. {wcp_library-1.2.8 → wcp_library-1.3.1}/README.md +0 -0
  22. {wcp_library-1.2.8 → wcp_library-1.3.1}/wcp_library/__init__.py +0 -0
  23. {wcp_library-1.2.8 → wcp_library-1.3.1}/wcp_library/credentials/__init__.py +0 -0
  24. {wcp_library-1.2.8 → wcp_library-1.3.1}/wcp_library/emailing.py +0 -0
  25. {wcp_library-1.2.8 → wcp_library-1.3.1}/wcp_library/ftp/__init__.py +0 -0
  26. {wcp_library-1.2.8 → wcp_library-1.3.1}/wcp_library/ftp/ftp.py +0 -0
  27. {wcp_library-1.2.8 → wcp_library-1.3.1}/wcp_library/ftp/sftp.py +0 -0
  28. {wcp_library-1.2.8 → wcp_library-1.3.1}/wcp_library/informatica.py +0 -0
  29. {wcp_library-1.2.8 → wcp_library-1.3.1}/wcp_library/logging.py +0 -0
  30. {wcp_library-1.2.8 → wcp_library-1.3.1}/wcp_library/selenium_helper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wcp-library
3
- Version: 1.2.8
3
+ Version: 1.3.1
4
4
  Summary: Common utilites for internal development at WCP
5
5
  Home-page: https://github.com/Whitecap-DNA/WCP-Library
6
6
  Author: Mitch-Petersen
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "wcp-library"
3
- version = "1.2.8"
3
+ version = "1.3.1"
4
4
  description = "Common utilites for internal development at WCP"
5
5
  authors = ["Mitch-Petersen <mitch.petersen@wcap.ca>"]
6
6
  readme = "README.md"
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from abc import ABC,abstractmethod
2
3
 
3
4
  import aiohttp
4
5
  from yarl import URL
@@ -8,12 +9,12 @@ from wcp_library.async_credentials import MissingCredentialsError
8
9
  logger = logging.getLogger(__name__)
9
10
 
10
11
 
11
- class AsyncOracleCredentialManager:
12
- def __init__(self, passwordState_api_key: str):
12
+ class AsyncCredentialManager(ABC):
13
+ def __init__(self, passwordState_api_key: str, password_list_id: int):
13
14
  self.password_url = URL("https://vault.wcap.ca/api/passwords/")
14
15
  self.api_key = passwordState_api_key
15
16
  self.headers = {"APIKey": self.api_key, 'Reason': 'Python Script Access'}
16
- self._password_list_id = 207
17
+ self._password_list_id = password_list_id
17
18
 
18
19
  async def _get_credentials(self) -> dict:
19
20
  """
@@ -40,6 +41,46 @@ class AsyncOracleCredentialManager:
40
41
  logger.debug("Credentials retrieved")
41
42
  return password_dict
42
43
 
44
+ async def _get_credential(self, password_id: int) -> dict:
45
+ """
46
+ Get a specific credential from the password list
47
+
48
+ :param password_id:
49
+ :return:
50
+ """
51
+
52
+ logger.debug(f"Getting credential with ID {password_id}")
53
+ url = (self.password_url / str(password_id))
54
+ async with aiohttp.ClientSession() as session:
55
+ async with session.get(str(url), headers=self.headers) as response:
56
+ password = await response.json()
57
+
58
+ if not password:
59
+ raise MissingCredentialsError(f"No credentials found with ID {password_id}")
60
+ password = password[0]
61
+
62
+ password_info = {'PasswordID': password['PasswordID'], 'UserName': password['UserName'], 'Password': password['Password']}
63
+ for field in password['GenericFieldInfo']:
64
+ password_info[field['DisplayName']] = field['Value'].lower() if field['DisplayName'].lower() == 'username' else field['Value']
65
+ return password_info
66
+
67
+ async def _publish_new_password(self, data: dict) -> bool:
68
+ """
69
+ Publish a new password to the password list
70
+
71
+ :param data:
72
+ :return:
73
+ """
74
+
75
+ async with aiohttp.ClientSession() as session:
76
+ async with session.post(str(self.password_url), json=data, headers=self.headers) as response:
77
+ if response.status == 201:
78
+ logger.debug(f"New credentials for {data['UserName']} created")
79
+ return True
80
+ else:
81
+ logger.error(f"Failed to create new credentials for {data['UserName']}")
82
+ return False
83
+
43
84
  async def get_credentials(self, username: str) -> dict:
44
85
  """
45
86
  Get the credentials for a specific username
@@ -94,38 +135,6 @@ class AsyncOracleCredentialManager:
94
135
  logger.error(f"Failed to update credentials for {credentials_dict['UserName']}")
95
136
  return False
96
137
 
138
+ @abstractmethod
97
139
  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
- - Service or SID
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['Service'] if 'Service' in credentials_dict else None,
121
- "GenericField4": credentials_dict['SID'] if 'SID' in credentials_dict else None
122
- }
123
-
124
- async with aiohttp.ClientSession() as session:
125
- async with session.post(str(self.password_url), json=data, headers=self.headers) as response:
126
- if response.status == 201:
127
- logger.debug(f"New credentials for {credentials_dict['UserName']} created")
128
- return True
129
- else:
130
- logger.error(f"Failed to create new credentials for {credentials_dict['UserName']}")
131
- return False
140
+ raise NotImplementedError("Must override in child class")
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from abc import ABC,abstractmethod
2
3
 
3
4
  import requests
4
5
  from yarl import URL
@@ -8,12 +9,12 @@ from wcp_library.credentials import MissingCredentialsError
8
9
  logger = logging.getLogger(__name__)
9
10
 
10
11
 
11
- class PostgresCredentialManager:
12
- def __init__(self, passwordState_api_key: str):
12
+ class CredentialManager(ABC):
13
+ def __init__(self, passwordState_api_key: str, password_list_id: int):
13
14
  self.password_url = URL("https://vault.wcap.ca/api/passwords/")
14
15
  self.api_key = passwordState_api_key
15
16
  self.headers = {"APIKey": self.api_key, 'Reason': 'Python Script Access'}
16
- self._password_list_id = 210
17
+ self._password_list_id = password_list_id
17
18
 
18
19
  def _get_credentials(self) -> dict:
19
20
  """
@@ -38,6 +39,44 @@ class PostgresCredentialManager:
38
39
  logger.debug("Credentials retrieved")
39
40
  return password_dict
40
41
 
42
+ def _get_credential(self, password_id: int) -> dict:
43
+ """
44
+ Get a specific credential from the password list
45
+
46
+ :param password_id:
47
+ :return:
48
+ """
49
+
50
+ logger.debug(f"Getting credential with ID {password_id}")
51
+ url = (self.password_url / str(password_id))
52
+ password = requests.get(str(url), headers=self.headers).json()
53
+
54
+ if not password:
55
+ raise MissingCredentialsError(f"No credentials found with ID {password_id}")
56
+ password = password[0]
57
+
58
+ password_info = {'PasswordID': password['PasswordID'], 'UserName': password['UserName'], 'Password': password['Password']}
59
+ for field in password['GenericFieldInfo']:
60
+ password_info[field['DisplayName']] = field['Value'].lower() if field['DisplayName'].lower() == 'username' else field['Value']
61
+ logger.debug("Credential retrieved")
62
+ return password_info
63
+
64
+ def _publish_new_password(self, data: dict) -> bool:
65
+ """
66
+ Publish a new password to the password list
67
+
68
+ :param data:
69
+ :return:
70
+ """
71
+
72
+ response = requests.post(str(self.password_url), json=data, headers=self.headers)
73
+ if response.status_code == 201:
74
+ logger.debug(f"New credentials for {data['UserName']} created")
75
+ return True
76
+ else:
77
+ logger.error(f"Failed to create new credentials for {data['UserName']}")
78
+ return False
79
+
41
80
  def get_credentials(self, username: str) -> dict:
42
81
  """
43
82
  Get the credentials for a specific username
@@ -56,6 +95,17 @@ class PostgresCredentialManager:
56
95
  logger.debug(f"Credentials for {username} retrieved")
57
96
  return return_credential
58
97
 
98
+ def get_credential_from_id(self, password_id: int) -> dict:
99
+ """
100
+ Get the credentials for a specific password ID
101
+
102
+ :param password_id:
103
+ :return:
104
+ """
105
+
106
+ return self._get_credential(password_id)
107
+
108
+
59
109
  def update_credential(self, credentials_dict: dict) -> bool:
60
110
  """
61
111
  Update the credentials for a specific username
@@ -89,36 +139,6 @@ class PostgresCredentialManager:
89
139
  logger.error(f"Failed to update credentials for {credentials_dict['UserName']}")
90
140
  return False
91
141
 
142
+ @abstractmethod
92
143
  def new_credentials(self, credentials_dict: dict) -> bool:
93
- """
94
- Create a new credential entry
95
-
96
- Credentials dictionary must have the following keys:
97
- - UserName
98
- - Password
99
- - Host
100
- - Port
101
- - Database
102
-
103
- :param credentials_dict:
104
- :return: True if successful, False otherwise
105
- """
106
-
107
- data = {
108
- "PasswordListID": self._password_list_id,
109
- "Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
110
- "Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
111
- "UserName": credentials_dict['UserName'].lower(),
112
- "Password": credentials_dict['Password'],
113
- "GenericField1": credentials_dict['Host'],
114
- "GenericField2": credentials_dict['Port'],
115
- "GenericField3": credentials_dict['Database']
116
- }
117
-
118
- response = requests.post(str(self.password_url), json=data, headers=self.headers)
119
- if response.status_code == 201:
120
- logger.debug(f"New credentials for {credentials_dict['UserName']} created")
121
- return True
122
- else:
123
- logger.error(f"Failed to create new credentials for {credentials_dict['UserName']}")
124
- return False
144
+ raise NotImplementedError("Must override in child class")
@@ -0,0 +1,72 @@
1
+ import logging
2
+
3
+ from wcp_library.credentials.credential_manager_asynchronous import AsyncCredentialManager
4
+ from wcp_library.credentials.credential_manager_synchronous import CredentialManager
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class FTPCredentialManager(CredentialManager):
10
+ def __init__(self, passwordState_api_key: str):
11
+ super().__init__(passwordState_api_key, 208)
12
+
13
+ def new_credentials(self, credentials_dict: dict) -> bool:
14
+ """
15
+ Create a new credential entry
16
+
17
+ Credentials dictionary must have the following keys:
18
+ - UserName
19
+ - Password
20
+ - Host
21
+ - Port
22
+ - FTP/SFTP (FTP or SFTP)
23
+
24
+ :param credentials_dict:
25
+ :return: True if successful, False otherwise
26
+ """
27
+
28
+ data = {
29
+ "PasswordListID": self._password_list_id,
30
+ "Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
31
+ "Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
32
+ "UserName": credentials_dict['UserName'].lower(),
33
+ "Password": credentials_dict['Password'],
34
+ "GenericField1": credentials_dict['Host'],
35
+ "GenericField2": credentials_dict['Port'],
36
+ "GenericField3": credentials_dict['FTP/SFTP']
37
+ }
38
+
39
+ return self._publish_new_password(data)
40
+
41
+
42
+ class AsyncFTPCredentialManager(AsyncCredentialManager):
43
+ def __init__(self, passwordState_api_key: str):
44
+ super().__init__(passwordState_api_key, 208)
45
+
46
+ async def new_credentials(self, credentials_dict: dict) -> bool:
47
+ """
48
+ Create a new credential entry
49
+
50
+ Credentials dictionary must have the following keys:
51
+ - UserName
52
+ - Password
53
+ - Host
54
+ - Port
55
+ - FTP/SFTP (FTP or SFTP)
56
+
57
+ :param credentials_dict:
58
+ :return:
59
+ """
60
+
61
+ data = {
62
+ "PasswordListID": self._password_list_id,
63
+ "Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
64
+ "Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
65
+ "UserName": credentials_dict['UserName'].lower(),
66
+ "Password": credentials_dict['Password'],
67
+ "GenericField1": credentials_dict['Host'],
68
+ "GenericField2": credentials_dict['Port'],
69
+ "GenericField3": credentials_dict['FTP/SFTP']
70
+ }
71
+
72
+ return await self._publish_new_password(data)
@@ -0,0 +1,77 @@
1
+ import logging
2
+
3
+ import aiohttp
4
+
5
+ from wcp_library.credentials.credential_manager_asynchronous import AsyncCredentialManager
6
+ from wcp_library.credentials.credential_manager_synchronous import CredentialManager
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class OracleCredentialManager(CredentialManager):
12
+ def __init__(self, passwordState_api_key: str):
13
+ super().__init__(passwordState_api_key, 207)
14
+
15
+ def new_credentials(self, credentials_dict: dict) -> bool:
16
+ """
17
+ Create a new credential entry
18
+
19
+ Credentials dictionary must have the following keys:
20
+ - UserName
21
+ - Password
22
+ - Host
23
+ - Port
24
+ - Service or SID
25
+
26
+ :param credentials_dict:
27
+ :return: True if successful, False otherwise
28
+ """
29
+
30
+ data = {
31
+ "PasswordListID": self._password_list_id,
32
+ "Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
33
+ "Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
34
+ "UserName": credentials_dict['UserName'].lower(),
35
+ "Password": credentials_dict['Password'],
36
+ "GenericField1": credentials_dict['Host'],
37
+ "GenericField2": credentials_dict['Port'],
38
+ "GenericField3": credentials_dict['Service'] if 'Service' in credentials_dict else None,
39
+ "GenericField4": credentials_dict['SID'] if 'SID' in credentials_dict else None
40
+ }
41
+
42
+ return self._publish_new_password(data)
43
+
44
+
45
+ class AsyncOracleCredentialManager(AsyncCredentialManager):
46
+ def __init__(self, passwordState_api_key: str):
47
+ super().__init__(passwordState_api_key, 207)
48
+
49
+ async def new_credentials(self, credentials_dict: dict) -> bool:
50
+ """
51
+ Create a new credential entry
52
+
53
+ Credentials dictionary must have the following keys:
54
+ - UserName
55
+ - Password
56
+ - Host
57
+ - Port
58
+ - Service or SID
59
+
60
+ :param credentials_dict:
61
+ :return:
62
+ """
63
+
64
+ data = {
65
+ "PasswordListID": self._password_list_id,
66
+ "Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
67
+ "Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
68
+ "UserName": credentials_dict['UserName'].lower(),
69
+ "Password": credentials_dict['Password'],
70
+ "GenericField1": credentials_dict['Host'],
71
+ "GenericField2": credentials_dict['Port'],
72
+ "GenericField3": credentials_dict['Service'] if 'Service' in credentials_dict else None,
73
+ "GenericField4": credentials_dict['SID'] if 'SID' in credentials_dict else None
74
+ }
75
+
76
+ return await self._publish_new_password(data)
77
+
@@ -0,0 +1,72 @@
1
+ import logging
2
+
3
+ from wcp_library.credentials.credential_manager_asynchronous import AsyncCredentialManager
4
+ from wcp_library.credentials.credential_manager_synchronous import CredentialManager
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class PostgresCredentialManager(CredentialManager):
10
+ def __init__(self, passwordState_api_key: str):
11
+ super().__init__(passwordState_api_key, 210)
12
+
13
+ def new_credentials(self, credentials_dict: dict) -> bool:
14
+ """
15
+ Create a new credential entry
16
+
17
+ Credentials dictionary must have the following keys:
18
+ - UserName
19
+ - Password
20
+ - Host
21
+ - Port
22
+ - Database
23
+
24
+ :param credentials_dict:
25
+ :return: True if successful, False otherwise
26
+ """
27
+
28
+ data = {
29
+ "PasswordListID": self._password_list_id,
30
+ "Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
31
+ "Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
32
+ "UserName": credentials_dict['UserName'].lower(),
33
+ "Password": credentials_dict['Password'],
34
+ "GenericField1": credentials_dict['Host'],
35
+ "GenericField2": credentials_dict['Port'],
36
+ "GenericField3": credentials_dict['Database']
37
+ }
38
+
39
+ return self._publish_new_password(data)
40
+
41
+
42
+ class AsyncPostgresCredentialManager(AsyncCredentialManager):
43
+ def __init__(self, passwordState_api_key: str):
44
+ super().__init__(passwordState_api_key, 210)
45
+
46
+ async def new_credentials(self, credentials_dict: dict) -> bool:
47
+ """
48
+ Create a new credential entry
49
+
50
+ Credentials dictionary must have the following keys:
51
+ - UserName
52
+ - Password
53
+ - Host
54
+ - Port
55
+ - Database
56
+
57
+ :param credentials_dict:
58
+ :return:
59
+ """
60
+
61
+ data = {
62
+ "PasswordListID": self._password_list_id,
63
+ "Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
64
+ "Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
65
+ "UserName": credentials_dict['UserName'].lower(),
66
+ "Password": credentials_dict['Password'],
67
+ "GenericField1": credentials_dict['Host'],
68
+ "GenericField2": credentials_dict['Port'],
69
+ "GenericField3": credentials_dict['Database']
70
+ }
71
+
72
+ return await self._publish_new_password(data)
@@ -1,6 +1,6 @@
1
- import asyncio
2
1
  import logging
3
2
  from functools import wraps
3
+ from time import sleep
4
4
 
5
5
  import oracledb
6
6
  import psycopg
@@ -16,6 +16,33 @@ def retry(f: callable) -> callable:
16
16
  :return: function
17
17
  """
18
18
 
19
+ @wraps(f)
20
+ def wrapper(self, *args, **kwargs):
21
+ self._retry_count = 0
22
+ while True:
23
+ try:
24
+ return 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("Oracle connection error")
30
+ logger.debug(error_obj.message)
31
+ logger.info("Waiting 5 minutes before retrying Oracle connection")
32
+ sleep(300)
33
+ else:
34
+ raise e
35
+ return wrapper
36
+
37
+
38
+ def async_retry(f: callable) -> callable:
39
+ """
40
+ Decorator to retry a function
41
+
42
+ :param f: function
43
+ :return: function
44
+ """
45
+
19
46
  @wraps(f)
20
47
  async def wrapper(self, *args, **kwargs):
21
48
  self._retry_count = 0