wcp-library 1.3.3__py3-none-any.whl → 1.3.5__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/credentials/{credential_manager_asynchronous.py → _credential_manager_asynchronous.py} +19 -4
- wcp_library/credentials/{credential_manager_synchronous.py → _credential_manager_synchronous.py} +6 -4
- wcp_library/credentials/api.py +74 -0
- wcp_library/credentials/ftp.py +2 -2
- wcp_library/credentials/internet.py +66 -0
- wcp_library/credentials/oracle.py +2 -2
- wcp_library/credentials/postgres.py +2 -2
- wcp_library/informatica.py +218 -110
- wcp_library/selenium_helper.py +11 -0
- wcp_library/time.py +68 -0
- {wcp_library-1.3.3.dist-info → wcp_library-1.3.5.dist-info}/METADATA +2 -1
- wcp_library-1.3.5.dist-info/RECORD +23 -0
- wcp_library-1.3.3.dist-info/RECORD +0 -20
- {wcp_library-1.3.3.dist-info → wcp_library-1.3.5.dist-info}/WHEEL +0 -0
wcp_library/credentials/{credential_manager_asynchronous.py → _credential_manager_asynchronous.py}
RENAMED
@@ -38,6 +38,8 @@ class AsyncCredentialManager(ABC):
|
|
38
38
|
for field in password['GenericFieldInfo']:
|
39
39
|
password_info[field['DisplayName']] = field['Value'].lower() if field['DisplayName'].lower() == 'username' else field['Value']
|
40
40
|
password_dict[password['UserName'].lower()] = password_info
|
41
|
+
if password['OTP']:
|
42
|
+
password_dict[password['UserName'].lower()]['OTP'] = password['OTP']
|
41
43
|
logger.debug("Credentials retrieved")
|
42
44
|
return password_dict
|
43
45
|
|
@@ -99,14 +101,24 @@ class AsyncCredentialManager(ABC):
|
|
99
101
|
logger.debug(f"Credentials for {username} retrieved")
|
100
102
|
return return_credential
|
101
103
|
|
104
|
+
async def get_credential_by_id(self, password_id: int) -> dict:
|
105
|
+
"""
|
106
|
+
Get the credentials for a specific Password ID
|
107
|
+
|
108
|
+
:param password_id:
|
109
|
+
:return:
|
110
|
+
"""
|
111
|
+
|
112
|
+
logger.debug(f"Getting credentials for ID {password_id}")
|
113
|
+
credential = await self._get_credential(password_id)
|
114
|
+
logger.debug(f"Credentials for ID {password_id} retrieved")
|
115
|
+
return credential
|
116
|
+
|
102
117
|
async def update_credential(self, credentials_dict: dict) -> bool:
|
103
118
|
"""
|
104
119
|
Update username and password in PasswordState
|
105
120
|
|
106
|
-
Credentials dictionary must
|
107
|
-
- PasswordID
|
108
|
-
- UserName
|
109
|
-
- Password
|
121
|
+
Credentials dictionary must the same keys as the original dictionary from the get_credentials method
|
110
122
|
|
111
123
|
The dictionary can be obtained from the get_credentials method
|
112
124
|
|
@@ -114,6 +126,9 @@ class AsyncCredentialManager(ABC):
|
|
114
126
|
:return:
|
115
127
|
"""
|
116
128
|
|
129
|
+
if "OTP" in credentials_dict:
|
130
|
+
credentials_dict.pop("OTP")
|
131
|
+
|
117
132
|
logger.debug(f"Updating credentials for {credentials_dict['UserName']}")
|
118
133
|
url = (self.password_url / str(self._password_list_id)).with_query("QueryAll")
|
119
134
|
async with aiohttp.ClientSession() as session:
|
wcp_library/credentials/{credential_manager_synchronous.py → _credential_manager_synchronous.py}
RENAMED
@@ -36,6 +36,8 @@ class CredentialManager(ABC):
|
|
36
36
|
for field in password['GenericFieldInfo']:
|
37
37
|
password_info[field['DisplayName']] = field['Value'].lower() if field['DisplayName'].lower() == 'username' else field['Value']
|
38
38
|
password_dict[password['UserName'].lower()] = password_info
|
39
|
+
if password['OTP']:
|
40
|
+
password_dict[password['UserName'].lower()]['OTP'] = password['OTP']
|
39
41
|
logger.debug("Credentials retrieved")
|
40
42
|
return password_dict
|
41
43
|
|
@@ -110,10 +112,7 @@ class CredentialManager(ABC):
|
|
110
112
|
"""
|
111
113
|
Update the credentials for a specific username
|
112
114
|
|
113
|
-
Credentials dictionary must
|
114
|
-
- PasswordID
|
115
|
-
- UserName
|
116
|
-
- Password
|
115
|
+
Credentials dictionary must the same keys as the original dictionary from the get_credentials method
|
117
116
|
|
118
117
|
The dictionary should be obtained from the get_credentials method and modified accordingly
|
119
118
|
|
@@ -121,6 +120,9 @@ class CredentialManager(ABC):
|
|
121
120
|
:return: True if successful, False otherwise
|
122
121
|
"""
|
123
122
|
|
123
|
+
if "OTP" in credentials_dict:
|
124
|
+
credentials_dict.pop("OTP")
|
125
|
+
|
124
126
|
logger.debug(f"Updating credentials for {credentials_dict['UserName']}")
|
125
127
|
url = (self.password_url / str(self._password_list_id)).with_query("QueryAll")
|
126
128
|
passwords = requests.get(str(url), headers=self.headers).json()
|
@@ -0,0 +1,74 @@
|
|
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 APICredentialManager(CredentialManager):
|
10
|
+
def __init__(self, passwordState_api_key: str):
|
11
|
+
super().__init__(passwordState_api_key, 214)
|
12
|
+
|
13
|
+
def new_credentials(self, credentials_dict: dict) -> bool:
|
14
|
+
"""
|
15
|
+
Create a new credential entry
|
16
|
+
|
17
|
+
Credentials dictionary can have the following keys:
|
18
|
+
- Title
|
19
|
+
- UserName
|
20
|
+
- Password
|
21
|
+
- API KEY
|
22
|
+
- Authentication Header
|
23
|
+
- URL
|
24
|
+
|
25
|
+
:param credentials_dict:
|
26
|
+
:return: True if successful, False otherwise
|
27
|
+
"""
|
28
|
+
|
29
|
+
data = {
|
30
|
+
"PasswordListID": self._password_list_id,
|
31
|
+
"Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
|
32
|
+
"Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
|
33
|
+
"UserName": credentials_dict['UserName'],
|
34
|
+
"Password": credentials_dict['Password'],
|
35
|
+
"GenericField1": credentials_dict['API KEY'],
|
36
|
+
"GenericField2": credentials_dict['Authentication Header'],
|
37
|
+
"URL": credentials_dict['URL']
|
38
|
+
}
|
39
|
+
|
40
|
+
return self._publish_new_password(data)
|
41
|
+
|
42
|
+
|
43
|
+
class AsyncAPICredentialManager(AsyncCredentialManager):
|
44
|
+
def __init__(self, passwordState_api_key: str):
|
45
|
+
super().__init__(passwordState_api_key, 214)
|
46
|
+
|
47
|
+
async def new_credentials(self, credentials_dict: dict) -> bool:
|
48
|
+
"""
|
49
|
+
Create a new credential entry
|
50
|
+
|
51
|
+
Credentials dictionary can have the following keys:
|
52
|
+
- Title
|
53
|
+
- UserName
|
54
|
+
- Password
|
55
|
+
- API KEY
|
56
|
+
- Authentication Header
|
57
|
+
- URL
|
58
|
+
|
59
|
+
:param credentials_dict:
|
60
|
+
:return:
|
61
|
+
"""
|
62
|
+
|
63
|
+
data = {
|
64
|
+
"PasswordListID": self._password_list_id,
|
65
|
+
"Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
|
66
|
+
"Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
|
67
|
+
"UserName": credentials_dict['UserName'],
|
68
|
+
"Password": credentials_dict['Password'],
|
69
|
+
"GenericField1": credentials_dict['API KEY'],
|
70
|
+
"GenericField2": credentials_dict['Authentication Header'],
|
71
|
+
"URL": credentials_dict['URL']
|
72
|
+
}
|
73
|
+
|
74
|
+
return await self._publish_new_password(data)
|
wcp_library/credentials/ftp.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import logging
|
2
2
|
|
3
|
-
from wcp_library.credentials.
|
4
|
-
from wcp_library.credentials.
|
3
|
+
from wcp_library.credentials._credential_manager_asynchronous import AsyncCredentialManager
|
4
|
+
from wcp_library.credentials._credential_manager_synchronous import CredentialManager
|
5
5
|
|
6
6
|
logger = logging.getLogger(__name__)
|
7
7
|
|
@@ -0,0 +1,66 @@
|
|
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 InternetCredentialManager(CredentialManager):
|
10
|
+
def __init__(self, passwordState_api_key: str):
|
11
|
+
super().__init__(passwordState_api_key, 93)
|
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
|
+
- Title
|
19
|
+
- UserName
|
20
|
+
- Password
|
21
|
+
- URL
|
22
|
+
|
23
|
+
:param credentials_dict:
|
24
|
+
:return:
|
25
|
+
"""
|
26
|
+
|
27
|
+
data = {
|
28
|
+
"PasswordListID": self._password_list_id,
|
29
|
+
"Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
|
30
|
+
"Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
|
31
|
+
"UserName": credentials_dict['UserName'].lower(),
|
32
|
+
"Password": credentials_dict['Password'],
|
33
|
+
"URL": credentials_dict['URL']
|
34
|
+
}
|
35
|
+
|
36
|
+
return self._publish_new_password(data)
|
37
|
+
|
38
|
+
|
39
|
+
class AsyncInternetCredentialManager(AsyncCredentialManager):
|
40
|
+
def __init__(self, passwordState_api_key: str):
|
41
|
+
super().__init__(passwordState_api_key, 93)
|
42
|
+
|
43
|
+
async def new_credentials(self, credentials_dict: dict) -> bool:
|
44
|
+
"""
|
45
|
+
Create a new credential entry
|
46
|
+
|
47
|
+
Credentials dictionary must have the following keys:
|
48
|
+
- Title
|
49
|
+
- UserName
|
50
|
+
- Password
|
51
|
+
- URL
|
52
|
+
|
53
|
+
:param credentials_dict:
|
54
|
+
:return:
|
55
|
+
"""
|
56
|
+
|
57
|
+
data = {
|
58
|
+
"PasswordListID": self._password_list_id,
|
59
|
+
"Title": credentials_dict['UserName'].upper() if "Title" not in credentials_dict else credentials_dict['Title'].upper(),
|
60
|
+
"Notes": credentials_dict['Notes'] if 'Notes' in credentials_dict else None,
|
61
|
+
"UserName": credentials_dict['UserName'].lower(),
|
62
|
+
"Password": credentials_dict['Password'],
|
63
|
+
"URL": credentials_dict['URL']
|
64
|
+
}
|
65
|
+
|
66
|
+
return await self._publish_new_password(data)
|
@@ -2,8 +2,8 @@ import logging
|
|
2
2
|
|
3
3
|
import aiohttp
|
4
4
|
|
5
|
-
from wcp_library.credentials.
|
6
|
-
from wcp_library.credentials.
|
5
|
+
from wcp_library.credentials._credential_manager_asynchronous import AsyncCredentialManager
|
6
|
+
from wcp_library.credentials._credential_manager_synchronous import CredentialManager
|
7
7
|
|
8
8
|
logger = logging.getLogger(__name__)
|
9
9
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import logging
|
2
2
|
|
3
|
-
from wcp_library.credentials.
|
4
|
-
from wcp_library.credentials.
|
3
|
+
from wcp_library.credentials._credential_manager_asynchronous import AsyncCredentialManager
|
4
|
+
from wcp_library.credentials._credential_manager_synchronous import CredentialManager
|
5
5
|
|
6
6
|
logger = logging.getLogger(__name__)
|
7
7
|
|
wcp_library/informatica.py
CHANGED
@@ -1,112 +1,220 @@
|
|
1
|
-
import
|
1
|
+
import logging
|
2
2
|
import json
|
3
|
-
import
|
4
|
-
|
3
|
+
import time
|
4
|
+
from datetime import datetime
|
5
|
+
from typing import Optional
|
6
|
+
|
7
|
+
import requests
|
8
|
+
from yarl import URL
|
9
|
+
|
10
|
+
from wcp_library.time import convert_tz
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
class InformaticaError(Exception):
|
16
|
+
pass
|
17
|
+
|
18
|
+
|
19
|
+
class InformaticaSession:
|
20
|
+
def __init__(self, username: str, password: str):
|
21
|
+
self.username: str = username
|
22
|
+
self.password: str = password
|
23
|
+
self._session_id: Optional[str] = None
|
24
|
+
self._server_url: Optional[URL] = None
|
25
|
+
|
26
|
+
self._get_session_id()
|
27
|
+
|
28
|
+
def _get_session_id(self) -> None:
|
29
|
+
"""
|
30
|
+
Authenticate with username and password
|
31
|
+
|
32
|
+
:return: icSessionId, serverUrl
|
33
|
+
"""
|
34
|
+
|
35
|
+
data = {'@type': 'login', 'username': self.username, 'password': self.password}
|
36
|
+
url = "https://dm-us.informaticacloud.com/ma/api/v2/user/login"
|
37
|
+
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
|
38
|
+
response = requests.post(url, data=json.dumps(data), headers=headers)
|
39
|
+
|
40
|
+
logger.debug('\tInformatica API Login Response Status Code: ' + str(response.status_code))
|
41
|
+
|
42
|
+
if response.status_code == 200:
|
43
|
+
logger.info('\tInformatica API Login Successful')
|
44
|
+
self._session_id = response.json()["icSessionId"]
|
45
|
+
self._server_url = URL(response.json()["serverUrl"])
|
46
|
+
else:
|
47
|
+
raise InformaticaError(f'\tInformatica API Login call failed: {response.status_code}')
|
5
48
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
49
|
+
def get_tasks(self, task_type: str) -> dict:
|
50
|
+
"""
|
51
|
+
Use this method to get a list of tasks of a specified type. This may be used to determine the TaskID of a task.
|
52
|
+
Task Types: https://jsapi.apiary.io/apis/cloudrestapi/reference/job/list-of-tasks/login.html
|
53
|
+
AVS-Contact validation task
|
54
|
+
DMASK-Data masking task
|
55
|
+
DQA-Data assessment task
|
56
|
+
DRS-Data replication task
|
57
|
+
DSS-Data synchronization task
|
58
|
+
MTT-Mapping configuration task
|
59
|
+
PCS-PowerCenter task
|
60
|
+
|
61
|
+
:param taskType: Task Type
|
62
|
+
:return: Task List
|
63
|
+
"""
|
64
|
+
|
65
|
+
task_list_url = self._server_url / "api/v2/task"
|
66
|
+
headers = {'icSessionId': self._session_id}
|
67
|
+
response = requests.get(str(task_list_url), headers=headers, params={'type': task_type})
|
68
|
+
|
69
|
+
logger.debug('\tRetrieved list of all Tasks')
|
70
|
+
|
71
|
+
if response.status_code == 200:
|
72
|
+
return json.loads(response.content)
|
73
|
+
else:
|
74
|
+
raise InformaticaError(f'\tFailed to get list of Tasks: {response.status_code}')
|
75
|
+
|
76
|
+
def get_task_id(self, task_name: str, task_type: str) -> str:
|
77
|
+
"""
|
78
|
+
Use this method to get the TaskID of a specified task. This may be used to run a task.
|
79
|
+
|
80
|
+
:param task_name: Task Name
|
81
|
+
:param task_type: Task Type
|
82
|
+
:return: Task ID
|
83
|
+
"""
|
84
|
+
|
85
|
+
tasks = self.get_tasks(task_type)
|
86
|
+
for task in tasks:
|
87
|
+
if task['name'] == task_name:
|
88
|
+
return task['id']
|
89
|
+
|
90
|
+
raise InformaticaError(f'\tFailed to find TaskID for the Task Name specified: {task_name}')
|
91
|
+
|
92
|
+
|
93
|
+
def is_task_running(self, task_id: str) -> tuple[bool, datetime]:
|
94
|
+
"""
|
95
|
+
Use this method to determine if a task is currently running.
|
96
|
+
|
97
|
+
:param task_id: Task ID
|
98
|
+
:return: Tuple of running status and startTime
|
99
|
+
"""
|
100
|
+
|
101
|
+
task_status_url = self._server_url / f"api/v2/task/{task_id}/status"
|
102
|
+
headers = {'icSessionId': self._session_id}
|
103
|
+
response = requests.get(str(task_status_url), headers=headers)
|
104
|
+
|
105
|
+
logger.debug(f'\tRetrieved status of Task {task_id}')
|
106
|
+
|
107
|
+
if response.status_code == 200:
|
108
|
+
task_status = json.loads(response.content)
|
109
|
+
utc_time = datetime.strptime(task_status['startTimeUTC'], '%Y-%m-%dT%H:%M:%S.%fZ')
|
110
|
+
local_time = convert_tz(utc_time, 'UTC')
|
111
|
+
return task_status['status'] == 'RUNNING', local_time
|
112
|
+
else:
|
113
|
+
raise InformaticaError(f'\tFailed to get status of Task: {response.status_code}')
|
114
|
+
|
115
|
+
def run_job(self, task_id: str, task_type: str) -> str:
|
116
|
+
"""
|
117
|
+
Use this method to run a task.
|
118
|
+
|
119
|
+
:param task_id: Task ID
|
120
|
+
:param task_type: Task Type
|
121
|
+
:return: Run ID
|
122
|
+
"""
|
123
|
+
|
124
|
+
job_start_url = self._server_url / "api/v2/job"
|
125
|
+
headers = {'Content-Type': 'application/json', 'icSessionId': self._session_id, 'Accept': 'application/json'}
|
126
|
+
data = {'@type': 'job', 'taskId': task_id, 'taskType': task_type}
|
127
|
+
response = requests.post(str(job_start_url), data=json.dumps(data), headers=headers)
|
128
|
+
|
129
|
+
if response.status_code == 200:
|
130
|
+
logger.info('Starting Informatica Job...')
|
131
|
+
response_dict = json.loads(response.content)
|
132
|
+
runID = response_dict['runId']
|
133
|
+
return runID
|
134
|
+
|
135
|
+
else:
|
136
|
+
raise InformaticaError(f"Failed to start Informatica Job: {response.status_code}")
|
137
|
+
|
138
|
+
def wait_until_job_finish(self, run_id: str) -> datetime:
|
139
|
+
"""
|
140
|
+
Use this method to wait until a job finishes running.
|
141
|
+
|
142
|
+
:param run_id:
|
143
|
+
:return: End Time
|
144
|
+
"""
|
145
|
+
|
146
|
+
job_status_url = self._server_url / f"api/v2/activity/activityLog"
|
147
|
+
headers = {'icSessionId': self._session_id}
|
148
|
+
|
149
|
+
while True:
|
150
|
+
response = requests.get(str(job_status_url), headers=headers, params={'runId': run_id})
|
151
|
+
if response.status_code == 200:
|
152
|
+
response_dict = json.loads(response.content)
|
153
|
+
|
154
|
+
if not response_dict['endTimeUtc']:
|
155
|
+
time.sleep(30)
|
156
|
+
continue
|
157
|
+
if response_dict['state'] == 1:
|
158
|
+
logger.info('\tJob completed successfully')
|
159
|
+
elif response_dict['state'] == 2:
|
160
|
+
logger.info('\tJob completed with errors')
|
161
|
+
elif response_dict['state'] == 3:
|
162
|
+
raise InformaticaError('Job failed')
|
163
|
+
return convert_tz(datetime.strptime(response_dict['endTimeUtc'], '%Y-%m-%dT%H:%M:%S.%fZ'), 'UTC')
|
164
|
+
else:
|
165
|
+
raise InformaticaError(f"Failed to get job status: {response.status_code}")
|
166
|
+
|
167
|
+
def get_connection_details(self) -> dict:
|
168
|
+
"""
|
169
|
+
Use this method to get a list of connections.
|
170
|
+
|
171
|
+
:return: Connection List
|
172
|
+
"""
|
173
|
+
|
174
|
+
connections_url = self._server_url / "api/v2/connection"
|
175
|
+
headers = {'icSessionId': self._session_id, 'Accept': 'application/json'}
|
176
|
+
response = requests.get(str(connections_url), headers=headers)
|
177
|
+
|
178
|
+
logger.debug('\tRetrieved list of all Connections')
|
179
|
+
|
180
|
+
if response.status_code == 200:
|
181
|
+
return json.loads(response.content)
|
182
|
+
else:
|
183
|
+
raise InformaticaError(f'\tFailed to get list of Connections: {response.status_code}')
|
184
|
+
|
185
|
+
def get_mapping_details(self, mapping_id: str) -> dict:
|
186
|
+
"""
|
187
|
+
Use this method to get details of a specific mapping.
|
188
|
+
|
189
|
+
:param mapping_id:
|
190
|
+
:return:
|
191
|
+
"""
|
192
|
+
|
193
|
+
mapping_details_url = self._server_url / f"api/v2/mapping/{mapping_id}"
|
194
|
+
headers = {'icSessionId': self._session_id, 'Accept': 'application/json'}
|
195
|
+
response = requests.get(str(mapping_details_url), headers=headers)
|
196
|
+
|
197
|
+
logger.debug(f'\tRetrieved details of Mapping {mapping_id}')
|
198
|
+
|
199
|
+
if response.status_code == 200:
|
200
|
+
return json.loads(response.content)
|
201
|
+
else:
|
202
|
+
raise InformaticaError(f'\tFailed to get details of Mapping {mapping_id}: {response.status_code}')
|
203
|
+
|
204
|
+
def get_all_mapping_details(self) -> dict:
|
205
|
+
"""
|
206
|
+
Use this method to get details of all mappings.
|
207
|
+
|
208
|
+
:return:
|
209
|
+
"""
|
210
|
+
|
211
|
+
mapping_details_url = self._server_url / "api/v2/mapping"
|
212
|
+
headers = {'icSessionId': self._session_id, 'Accept': 'application/json'}
|
213
|
+
response = requests.get(str(mapping_details_url), headers=headers)
|
214
|
+
|
215
|
+
logger.debug('\tRetrieved details of all Mappings')
|
216
|
+
|
217
|
+
if response.status_code == 200:
|
218
|
+
return json.loads(response.content)
|
219
|
+
else:
|
220
|
+
raise InformaticaError(f'\tFailed to get details of all Mappings: {response.status_code}')
|
wcp_library/selenium_helper.py
CHANGED
@@ -75,3 +75,14 @@ class SeleniumHelper:
|
|
75
75
|
"""
|
76
76
|
|
77
77
|
WebDriverWait(self.driver, timeout).until(expected_conditions.presence_of_element_located((By.CSS_SELECTOR, css_element)))
|
78
|
+
|
79
|
+
def wait_until_clickable(self, css_element: str, timeout: int = 30) -> None:
|
80
|
+
"""
|
81
|
+
Wait until an element is clickable
|
82
|
+
|
83
|
+
:param css_element:
|
84
|
+
:param timeout:
|
85
|
+
:return:
|
86
|
+
"""
|
87
|
+
|
88
|
+
WebDriverWait(self.driver, timeout).until(expected_conditions.element_to_be_clickable((By.CSS_SELECTOR, css_element)))
|
wcp_library/time.py
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
|
3
|
+
import pytz
|
4
|
+
|
5
|
+
|
6
|
+
def get_current_time(aware: bool=False, tz: str='Canada/Mountain') -> datetime:
|
7
|
+
"""
|
8
|
+
Get the current time (Mountain Time)
|
9
|
+
|
10
|
+
you can find a list of timezones by printing pytz.all_timezones
|
11
|
+
|
12
|
+
:param aware:
|
13
|
+
:param tz:
|
14
|
+
:return:
|
15
|
+
"""
|
16
|
+
|
17
|
+
tz = pytz.timezone(tz)
|
18
|
+
current_time = datetime.now(tz)
|
19
|
+
|
20
|
+
if not aware:
|
21
|
+
return current_time.replace(tzinfo=None)
|
22
|
+
return current_time
|
23
|
+
|
24
|
+
|
25
|
+
def convert_tz(time: datetime, original_tz: str, aware: bool=False, tz: str='Canada/Mountain') -> datetime:
|
26
|
+
"""
|
27
|
+
Convert time to a different timezone
|
28
|
+
|
29
|
+
you can find a list of timezones by printing pytz.all_timezones
|
30
|
+
|
31
|
+
:param time:
|
32
|
+
:param original_tz:
|
33
|
+
:param aware:
|
34
|
+
:param tz:
|
35
|
+
:return:
|
36
|
+
"""
|
37
|
+
|
38
|
+
time = time.replace(tzinfo=pytz.timezone(original_tz))
|
39
|
+
converted_time = time.astimezone(pytz.timezone(tz))
|
40
|
+
return converted_time if aware else converted_time.replace(tzinfo=None)
|
41
|
+
|
42
|
+
def get_utc_timestamp(time: datetime, original_tz: str='Canada/Mountain') -> int:
|
43
|
+
"""
|
44
|
+
Get the UTC timestamp of a datetime object
|
45
|
+
|
46
|
+
you can find a list of timezones by printing pytz.all_timezones
|
47
|
+
|
48
|
+
:param time:
|
49
|
+
:param original_tz:
|
50
|
+
:return:
|
51
|
+
"""
|
52
|
+
|
53
|
+
converted_time = convert_tz(time, original_tz, aware=False, tz='UTC')
|
54
|
+
return int(converted_time.timestamp())
|
55
|
+
|
56
|
+
def get_local_timestamp(time: datetime, original_tz: str='Canada/Mountain') -> int:
|
57
|
+
"""
|
58
|
+
Get the local timestamp of a datetime object
|
59
|
+
|
60
|
+
you can find a list of timezones by printing pytz.all_timezones
|
61
|
+
|
62
|
+
:param time:
|
63
|
+
:param original_tz:
|
64
|
+
:return:
|
65
|
+
"""
|
66
|
+
|
67
|
+
converted_time = convert_tz(time, original_tz, aware=False)
|
68
|
+
return int(converted_time.timestamp())
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: wcp-library
|
3
|
-
Version: 1.3.
|
3
|
+
Version: 1.3.5
|
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
|
@@ -19,6 +19,7 @@ Requires-Dist: psycopg (>=3.2.3,<4.0.0)
|
|
19
19
|
Requires-Dist: psycopg-binary (>=3.2.3,<4.0.0)
|
20
20
|
Requires-Dist: psycopg-pool (>=3.2.3,<4.0.0)
|
21
21
|
Requires-Dist: pycryptodome (>=3.21.0,<4.0.0)
|
22
|
+
Requires-Dist: pytz (>=2024.2,<2025.0)
|
22
23
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
23
24
|
Requires-Dist: selenium (>=4.27.1,<5.0.0)
|
24
25
|
Requires-Dist: webdriver-manager (>=4.0.2,<5.0.0)
|
@@ -0,0 +1,23 @@
|
|
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=XLYZ6XjLU3Cp18cin3BidIBPvjoqqdJ5L7hhueMCfpU,6311
|
4
|
+
wcp_library/credentials/_credential_manager_synchronous.py,sha256=5QyIYYPM2HUlApdZl6ELypuuCioi6Z-JHwyJWFrwYBs,5685
|
5
|
+
wcp_library/credentials/api.py,sha256=NZfZcN46-ZVB_-DatR0DLBYAtJxulDlKD2cculzbyXo,2625
|
6
|
+
wcp_library/credentials/ftp.py,sha256=lSjOlfU68j9PsOCaoWJSdjbR1QmjTxjVmSxOpMdMG7Q,2591
|
7
|
+
wcp_library/credentials/internet.py,sha256=z9tihFddTJcX2pt-iHFP_rkCoheLhTdFgn4ksc2sYYE,2239
|
8
|
+
wcp_library/credentials/oracle.py,sha256=OV02op95LNUT21cNDM4zDbD63C1ldiq--vYofDpU-zU,2869
|
9
|
+
wcp_library/credentials/postgres.py,sha256=B5qNPKYzuzOJZ5S4M3MCN4xUelvZ6AgyR6QJc4z0Cgo,2573
|
10
|
+
wcp_library/emailing.py,sha256=xqNly6Tmj-pvwl5bdys3gauZFDd4SuWCQYzGFNemv2Q,2496
|
11
|
+
wcp_library/ftp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
wcp_library/ftp/ftp.py,sha256=EpyW0J2QIGxP8zVGD4VarA0hi4C2XAPDPF-0j2sRdpI,4350
|
13
|
+
wcp_library/ftp/sftp.py,sha256=hykXGLGdxe7DYAxFdTwjPjTEOYuIpSMyK3NOiTQNUK0,4176
|
14
|
+
wcp_library/informatica.py,sha256=_g9lzSd-JLsY4e5_g6YcxQtOxP6fkg4sGfvNJaAN7jY,8123
|
15
|
+
wcp_library/logging.py,sha256=e6gG7HFgUrMajUZs4Gs0atFfOJJmdmxN0GerfynNWlY,2061
|
16
|
+
wcp_library/selenium_helper.py,sha256=2PENUH8bgpyzovbLjP8a4Oyd7c3yOwoyQJt-a8-zq0c,2855
|
17
|
+
wcp_library/sql/__init__.py,sha256=s2psmwkq_ZU23iGWvXjJrLu0hD1fB6CDv6RHcK7y828,1917
|
18
|
+
wcp_library/sql/oracle.py,sha256=TGiTC5L5UcM5QcHFajgn43NI8HygOGIEAtLmLbVFp2I,15772
|
19
|
+
wcp_library/sql/postgres.py,sha256=GElSjLDworrRlzA7DLrbcQlW7BqvhgMbvW89v-yZejk,14533
|
20
|
+
wcp_library/time.py,sha256=ktSzhK7SZnGPNXobFexnhFGQAcriLCJQKxnO0fed8fQ,1740
|
21
|
+
wcp_library-1.3.5.dist-info/METADATA,sha256=hnu1wsVA8QLeWwq7wFNuYu_eZH3R3CqCot1_NWQ4D60,1552
|
22
|
+
wcp_library-1.3.5.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
23
|
+
wcp_library-1.3.5.dist-info/RECORD,,
|
@@ -1,20 +0,0 @@
|
|
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=avAvolbtE1qPTTaUas_fMX7Oj_fiBUMutAFztRDDKJI,5722
|
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=GElSjLDworrRlzA7DLrbcQlW7BqvhgMbvW89v-yZejk,14533
|
18
|
-
wcp_library-1.3.3.dist-info/METADATA,sha256=OsZJWIWZQ8iDOGtljfPsdpA4jQK8Chj1YihteHZwnQ4,1513
|
19
|
-
wcp_library-1.3.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
20
|
-
wcp_library-1.3.3.dist-info/RECORD,,
|
File without changes
|