brynq-sdk-brynq 4.2.6.dev0__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 brynq-sdk-brynq might be problematic. Click here for more details.
- brynq_sdk_brynq/__init__.py +1 -0
- brynq_sdk_brynq/brynq.py +289 -0
- brynq_sdk_brynq/credentials.py +157 -0
- brynq_sdk_brynq/customers.py +88 -0
- brynq_sdk_brynq/interfaces.py +234 -0
- brynq_sdk_brynq/mappings.py +107 -0
- brynq_sdk_brynq/organization_chart.py +251 -0
- brynq_sdk_brynq/roles.py +272 -0
- brynq_sdk_brynq/scenarios.py +3495 -0
- brynq_sdk_brynq/schemas/__init__.py +52 -0
- brynq_sdk_brynq/schemas/credentials.py +37 -0
- brynq_sdk_brynq/schemas/customers.py +108 -0
- brynq_sdk_brynq/schemas/interfaces.py +237 -0
- brynq_sdk_brynq/schemas/organization_chart.py +70 -0
- brynq_sdk_brynq/schemas/roles.py +95 -0
- brynq_sdk_brynq/schemas/scenarios.py +419 -0
- brynq_sdk_brynq/schemas/users.py +126 -0
- brynq_sdk_brynq/source_systems.py +175 -0
- brynq_sdk_brynq/users.py +405 -0
- brynq_sdk_brynq-4.2.6.dev0.dist-info/METADATA +17 -0
- brynq_sdk_brynq-4.2.6.dev0.dist-info/RECORD +23 -0
- brynq_sdk_brynq-4.2.6.dev0.dist-info/WHEEL +5 -0
- brynq_sdk_brynq-4.2.6.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .brynq import BrynQ
|
brynq_sdk_brynq/brynq.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import warnings
|
|
5
|
+
from typing import Union, Literal, Optional, List, Dict, Any
|
|
6
|
+
from .users import Users
|
|
7
|
+
from .organization_chart import OrganizationChart
|
|
8
|
+
from .source_systems import SourceSystems
|
|
9
|
+
from .customers import Customers
|
|
10
|
+
from .interfaces import Interfaces
|
|
11
|
+
from .roles import Roles
|
|
12
|
+
from requests.adapters import HTTPAdapter
|
|
13
|
+
from urllib3.util.retry import Retry
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BrynQ:
|
|
17
|
+
def __init__(self, subdomain: str = None, api_token: str = None, staging: str = 'prod'):
|
|
18
|
+
self.subdomain = os.getenv("BRYNQ_SUBDOMAIN", subdomain)
|
|
19
|
+
self.api_token = os.getenv("BRYNQ_API_TOKEN", api_token)
|
|
20
|
+
self.environment = os.getenv("BRYNQ_ENVIRONMENT", staging)
|
|
21
|
+
self.timeout = 3600
|
|
22
|
+
self.data_interface_id = os.getenv("DATA_INTERFACE_ID")
|
|
23
|
+
if self.data_interface_id is None:
|
|
24
|
+
raise ValueError("BRYNQ_DATA_INTERFACE_ID environment variable is not set, you should use this class via the TaskScheduler or set the variable in your code with:"
|
|
25
|
+
"os.environ['DATA_INTERFACE_ID'] = str(self.data_interface_id). This is better than setting it in your .env where you will have to change it when switching between interfaces.")
|
|
26
|
+
|
|
27
|
+
if any([self.subdomain is None, self.api_token is None]):
|
|
28
|
+
raise ValueError("Set the subdomain, api_token either in your .env file or provide the subdomain and api_token parameters")
|
|
29
|
+
|
|
30
|
+
possible_environments = ['dev', 'prod']
|
|
31
|
+
if self.environment not in possible_environments:
|
|
32
|
+
raise ValueError(f"Environment should be in {','.join(possible_environments)}")
|
|
33
|
+
|
|
34
|
+
self.url = 'https://app.brynq-staging.com/api/v2/' if self.environment == 'dev' else 'https://app.brynq.com/api/v2/'
|
|
35
|
+
|
|
36
|
+
# Initialize session with retry strategy. This is called brynq_session and not session as to not conflict with other SDKs that use self.session
|
|
37
|
+
self.brynq_session = requests.Session()
|
|
38
|
+
retry_strategy = Retry(
|
|
39
|
+
total=3, # number of retries
|
|
40
|
+
backoff_factor=0.5, # wait 0.5s * (2 ** (retry - 1)) between retries
|
|
41
|
+
status_forcelist=[500, 502, 503, 504] # HTTP status codes to retry on
|
|
42
|
+
)
|
|
43
|
+
adapter = HTTPAdapter(max_retries=retry_strategy)
|
|
44
|
+
self.brynq_session.mount("http://", adapter)
|
|
45
|
+
self.brynq_session.mount("https://", adapter)
|
|
46
|
+
self.brynq_session.headers.update(self._get_headers())
|
|
47
|
+
|
|
48
|
+
# Initialize components
|
|
49
|
+
self.users = Users(self)
|
|
50
|
+
self.organization_chart = OrganizationChart(self)
|
|
51
|
+
self.source_systems = SourceSystems(self)
|
|
52
|
+
self.customers = Customers(self)
|
|
53
|
+
self.interfaces = Interfaces(self)
|
|
54
|
+
self.roles = Roles(self)
|
|
55
|
+
|
|
56
|
+
def _get_headers(self):
|
|
57
|
+
return {
|
|
58
|
+
'Authorization': f'Bearer {self.api_token}',
|
|
59
|
+
'Domain': self.subdomain
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def get_mapping(self, mapping: str, return_format: Literal['input_as_key', 'columns_names_as_keys', 'nested_input_output'] = 'input_as_key') -> dict:
|
|
63
|
+
"""
|
|
64
|
+
DEPRECATED: Use brynq.mappings.get_mapping() instead
|
|
65
|
+
"""
|
|
66
|
+
warnings.warn(
|
|
67
|
+
"This method is deprecated. Use brynq.interfaces.mappings.get() instead",
|
|
68
|
+
DeprecationWarning,
|
|
69
|
+
stacklevel=2
|
|
70
|
+
)
|
|
71
|
+
"""
|
|
72
|
+
Get the mapping json from the mappings
|
|
73
|
+
:param data_interface_id: The id of the task in BrynQ. this does not have to be the task id of the current task
|
|
74
|
+
:param mapping: The name of the mapping
|
|
75
|
+
:param return_format: Determines how the mapping should be returned. Options are 'input_as_key' (Default, the input column is the key, the output columns are the values), 'columns_names_as_keys', 'nested_input_output'
|
|
76
|
+
:return: The json of the mapping
|
|
77
|
+
"""
|
|
78
|
+
# Find the mapping for the given sheet name
|
|
79
|
+
mappings = self.interfaces.mappings._get_mappings()
|
|
80
|
+
mapping_data = next((item for item in mappings if item['name'] == mapping), None)
|
|
81
|
+
if not mapping_data:
|
|
82
|
+
raise ValueError(f"Mapping named '{mapping}' not found")
|
|
83
|
+
|
|
84
|
+
# If the user want to get the column names back as keys, transform the data accordingly and return
|
|
85
|
+
if return_format == 'columns_names_as_keys':
|
|
86
|
+
final_mapping = []
|
|
87
|
+
for row in mapping_data['values']:
|
|
88
|
+
combined_dict = {}
|
|
89
|
+
combined_dict.update(row['input'])
|
|
90
|
+
combined_dict.update(row['output'])
|
|
91
|
+
final_mapping.append(combined_dict)
|
|
92
|
+
elif return_format == 'nested_input_output':
|
|
93
|
+
final_mapping = mapping_data
|
|
94
|
+
else:
|
|
95
|
+
final_mapping = {}
|
|
96
|
+
for value in mapping_data['values']:
|
|
97
|
+
input_values = []
|
|
98
|
+
output_values = []
|
|
99
|
+
for _, val in value['input'].items():
|
|
100
|
+
input_values.append(val)
|
|
101
|
+
for _, val in value['output'].items():
|
|
102
|
+
output_values.append(val)
|
|
103
|
+
# Detect if there are multiple input or output columns and concatenate them
|
|
104
|
+
if len(value['input'].items()) > 1 or len(value['output'].items()) > 1:
|
|
105
|
+
concatenated_input = ','.join(input_values)
|
|
106
|
+
concatenated_output = ','.join(output_values)
|
|
107
|
+
final_mapping[concatenated_input] = concatenated_output
|
|
108
|
+
else: # Default to assuming there's only one key-value pair if not concatenating
|
|
109
|
+
if output_values:
|
|
110
|
+
final_mapping[input_values[0]] = output_values[0]
|
|
111
|
+
|
|
112
|
+
return final_mapping
|
|
113
|
+
|
|
114
|
+
def get_mapping_as_dataframe(self, mapping: str, prefix: bool = False) -> pd.DataFrame:
|
|
115
|
+
"""
|
|
116
|
+
DEPRECATED: Use brynq.mappings.get_mapping_as_dataframe() instead
|
|
117
|
+
"""
|
|
118
|
+
warnings.warn(
|
|
119
|
+
"This method is deprecated. Use brynq.interfaces.mappings.get(as_df = True) instead",
|
|
120
|
+
DeprecationWarning,
|
|
121
|
+
stacklevel=2
|
|
122
|
+
)
|
|
123
|
+
"""
|
|
124
|
+
Get the mapping dataframe from the mappings
|
|
125
|
+
:param mapping: The name of the mapping
|
|
126
|
+
:param prefix: A boolean to indicate if the keys should be prefixed with 'input.' and 'output.'
|
|
127
|
+
:return: The dataframe of the mapping
|
|
128
|
+
"""
|
|
129
|
+
# Find the mapping for the given sheet name
|
|
130
|
+
mappings = self.interfaces.mappings._get_mappings()
|
|
131
|
+
mapping_data = next((item for item in mappings if item['name'] == mapping), None)
|
|
132
|
+
if not mapping_data:
|
|
133
|
+
raise ValueError(f"Mapping named '{mapping}' not found")
|
|
134
|
+
|
|
135
|
+
# Extract the values which contain the input-output mappings
|
|
136
|
+
values = mapping_data['values']
|
|
137
|
+
|
|
138
|
+
# Create a list to hold all row data
|
|
139
|
+
rows = []
|
|
140
|
+
for value in values:
|
|
141
|
+
# Check if prefix is needed and adjust keys accordingly
|
|
142
|
+
if prefix:
|
|
143
|
+
input_data = {f'input.{key}': val for key, val in value['input'].items()}
|
|
144
|
+
output_data = {f'output.{key}': val for key, val in value['output'].items()}
|
|
145
|
+
else:
|
|
146
|
+
input_data = value['input']
|
|
147
|
+
output_data = value['output']
|
|
148
|
+
|
|
149
|
+
# Combine 'input' and 'output' dictionaries
|
|
150
|
+
row_data = {**input_data, **output_data}
|
|
151
|
+
rows.append(row_data)
|
|
152
|
+
|
|
153
|
+
# Create DataFrame from rows
|
|
154
|
+
df = pd.DataFrame(rows)
|
|
155
|
+
|
|
156
|
+
return df
|
|
157
|
+
|
|
158
|
+
def get_system_credential(self, system: str, label: Union[str, list], test_environment: bool = False) -> dict:
|
|
159
|
+
"""
|
|
160
|
+
DEPRECATED: Use brynq.credentials.get() instead
|
|
161
|
+
"""
|
|
162
|
+
warnings.warn(
|
|
163
|
+
"This method is deprecated. Use brynq.credentials.get() instead",
|
|
164
|
+
DeprecationWarning,
|
|
165
|
+
stacklevel=2
|
|
166
|
+
)
|
|
167
|
+
return self.interfaces.credentials.get_system_credential(system, label, test_environment)
|
|
168
|
+
|
|
169
|
+
def get_interface_credential(self, system: str, system_type: Optional[str] = None,
|
|
170
|
+
test_environment: bool = False) -> Union[dict, List[dict]]:
|
|
171
|
+
"""
|
|
172
|
+
DEPRECATED: Use brynq.credentials.get_interface_credential() instead
|
|
173
|
+
"""
|
|
174
|
+
warnings.warn(
|
|
175
|
+
"This method is deprecated. Use brynq.credentials.get() instead",
|
|
176
|
+
DeprecationWarning,
|
|
177
|
+
stacklevel=2
|
|
178
|
+
)
|
|
179
|
+
return self.interfaces.credentials.get(self.data_interface_id, system, system_type, test_environment)
|
|
180
|
+
|
|
181
|
+
def get_user_data(self):
|
|
182
|
+
"""
|
|
183
|
+
DEPRECATED: Use brynq.users.get_user_data() instead
|
|
184
|
+
"""
|
|
185
|
+
warnings.warn(
|
|
186
|
+
"This method is deprecated. Use brynq.users.get_user_data() instead",
|
|
187
|
+
DeprecationWarning,
|
|
188
|
+
stacklevel=2
|
|
189
|
+
)
|
|
190
|
+
return self.users.get()
|
|
191
|
+
|
|
192
|
+
def get_user_authorization_qlik_app(self, dashboard_id):
|
|
193
|
+
"""
|
|
194
|
+
DEPRECATED: Use brynq.users.get_user_authorization_qlik_app() instead
|
|
195
|
+
"""
|
|
196
|
+
warnings.warn(
|
|
197
|
+
"This method is deprecated. Use brynq.users.get_user_authorization_qlik_app() instead",
|
|
198
|
+
DeprecationWarning,
|
|
199
|
+
stacklevel=2
|
|
200
|
+
)
|
|
201
|
+
return self.users.get_user_authorization_qlik_app(dashboard_id)
|
|
202
|
+
|
|
203
|
+
def get_role_data(self):
|
|
204
|
+
"""
|
|
205
|
+
DEPRECATED: Use brynq.users.get_role_data() instead
|
|
206
|
+
"""
|
|
207
|
+
warnings.warn(
|
|
208
|
+
"This method is deprecated. Use brynq.users.get_role_data() instead",
|
|
209
|
+
DeprecationWarning,
|
|
210
|
+
stacklevel=2
|
|
211
|
+
)
|
|
212
|
+
return self.roles.get()
|
|
213
|
+
|
|
214
|
+
def create_user(self, user_data: dict) -> requests.Response:
|
|
215
|
+
"""
|
|
216
|
+
DEPRECATED: Use brynq.users.create_user() instead
|
|
217
|
+
"""
|
|
218
|
+
warnings.warn(
|
|
219
|
+
"This method is deprecated. Use brynq.users.create_user() instead",
|
|
220
|
+
DeprecationWarning,
|
|
221
|
+
stacklevel=2
|
|
222
|
+
)
|
|
223
|
+
return self.users.invite(user_data)
|
|
224
|
+
|
|
225
|
+
def update_user(self, user_id: str, user_data: dict) -> requests.Response:
|
|
226
|
+
"""
|
|
227
|
+
DEPRECATED: Use brynq.users.update_user() instead
|
|
228
|
+
"""
|
|
229
|
+
warnings.warn(
|
|
230
|
+
"This method is deprecated. Use brynq.users.update_user() instead",
|
|
231
|
+
DeprecationWarning,
|
|
232
|
+
stacklevel=2
|
|
233
|
+
)
|
|
234
|
+
return self.users.update(user_id, user_data)
|
|
235
|
+
|
|
236
|
+
def delete_user(self, user_id: str) -> requests.Response:
|
|
237
|
+
"""
|
|
238
|
+
DEPRECATED: Use brynq.users.delete_user() instead
|
|
239
|
+
"""
|
|
240
|
+
warnings.warn(
|
|
241
|
+
"This method is deprecated. Use brynq.users.delete_user() instead",
|
|
242
|
+
DeprecationWarning,
|
|
243
|
+
stacklevel=2
|
|
244
|
+
)
|
|
245
|
+
return self.users.delete(user_id)
|
|
246
|
+
|
|
247
|
+
def overwrite_user_roles(self, roles: dict) -> requests.Response:
|
|
248
|
+
"""
|
|
249
|
+
DEPRECATED: Use brynq.users.overwrite_user_roles() instead
|
|
250
|
+
"""
|
|
251
|
+
warnings.warn(
|
|
252
|
+
"This method is deprecated. Use brynq.users.overwrite_user_roles() instead",
|
|
253
|
+
DeprecationWarning,
|
|
254
|
+
stacklevel=2
|
|
255
|
+
)
|
|
256
|
+
return self.roles.update(roles)
|
|
257
|
+
|
|
258
|
+
def get_source_system_entities(self, system: int) -> requests.Response:
|
|
259
|
+
"""
|
|
260
|
+
DEPRECATED: Use brynq.source_systems.get_entities() instead
|
|
261
|
+
"""
|
|
262
|
+
warnings.warn(
|
|
263
|
+
"This method is deprecated. Use brynq.source_systems.get_entities() instead",
|
|
264
|
+
DeprecationWarning,
|
|
265
|
+
stacklevel=2
|
|
266
|
+
)
|
|
267
|
+
return self.source_systems.get_entities(system)
|
|
268
|
+
|
|
269
|
+
def get_layers(self) -> List[Dict[str, Any]]:
|
|
270
|
+
"""
|
|
271
|
+
DEPRECATED: Use brynq.organization_chart.get_layers() instead
|
|
272
|
+
"""
|
|
273
|
+
warnings.warn(
|
|
274
|
+
"This method is deprecated. Use brynq.organization_chart.get_layers() instead",
|
|
275
|
+
DeprecationWarning,
|
|
276
|
+
stacklevel=2
|
|
277
|
+
)
|
|
278
|
+
return self.organization_chart.get_layers()
|
|
279
|
+
|
|
280
|
+
def __enter__(self):
|
|
281
|
+
return self
|
|
282
|
+
|
|
283
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
284
|
+
self.close()
|
|
285
|
+
|
|
286
|
+
def close(self):
|
|
287
|
+
"""Close the session and cleanup resources"""
|
|
288
|
+
if hasattr(self, 'session'):
|
|
289
|
+
self.brynq_session.close()
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
from typing import Optional, Union, List, Dict, Any
|
|
4
|
+
import warnings
|
|
5
|
+
from .schemas.credentials import CredentialsConfig
|
|
6
|
+
from brynq_sdk_functions import Functions
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Credentials:
|
|
10
|
+
"""
|
|
11
|
+
Handles all credential-related operations for BrynQ SDK.
|
|
12
|
+
"""
|
|
13
|
+
def __init__(self, brynq_instance):
|
|
14
|
+
"""
|
|
15
|
+
Initialize Credentials manager.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
brynq_instance: The parent BrynQ instance
|
|
19
|
+
"""
|
|
20
|
+
self._brynq = brynq_instance
|
|
21
|
+
|
|
22
|
+
def get_system_credential(self, system: str, label: Union[str, list], test_environment: bool = False) -> dict:
|
|
23
|
+
"""
|
|
24
|
+
DEPRECATED: Use brynq.interfaces.credentials.get() instead
|
|
25
|
+
"""
|
|
26
|
+
warnings.warn("This function is deprecated and will be removed in a future version.", DeprecationWarning, stacklevel=2)
|
|
27
|
+
response = self._brynq.brynq_session.get(
|
|
28
|
+
url=f'{self._brynq.url}apps/{system}',
|
|
29
|
+
timeout=self._brynq.timeout
|
|
30
|
+
)
|
|
31
|
+
response.raise_for_status()
|
|
32
|
+
credentials = response.json()
|
|
33
|
+
# rename parameter for readability
|
|
34
|
+
if isinstance(label, str):
|
|
35
|
+
labels = [label]
|
|
36
|
+
else:
|
|
37
|
+
labels = label
|
|
38
|
+
# filter credentials based on label. All labels specified in label parameter should be present in the credential object
|
|
39
|
+
credentials = [credential for credential in credentials if all(label in credential['labels'] for label in labels)]
|
|
40
|
+
if system == 'profit':
|
|
41
|
+
credentials = [credential for credential in credentials if credential['isTestEnvironment'] is test_environment]
|
|
42
|
+
|
|
43
|
+
if len(credentials) == 0:
|
|
44
|
+
raise ValueError(f'No credentials found for {system}')
|
|
45
|
+
if len(credentials) != 1:
|
|
46
|
+
raise ValueError(f'Multiple credentials found for {system} with the specified labels')
|
|
47
|
+
|
|
48
|
+
return credentials[0]
|
|
49
|
+
|
|
50
|
+
def get(self, system: str, system_type: Optional[str] = None, test_environment: bool = False) -> Union[dict, List[dict]]:
|
|
51
|
+
"""
|
|
52
|
+
This method retrieves authentication credentials from BrynQ for a specific interface and system.
|
|
53
|
+
|
|
54
|
+
:param interface_id: ID of the interface to get credentials for
|
|
55
|
+
:param system: The app name to search for in credentials (e.g., 'bob', 'profit')
|
|
56
|
+
:param system_type: Optional parameter to specify 'source' or 'target'. If not provided,
|
|
57
|
+
searches in both lists
|
|
58
|
+
:param test_environment: boolean indicating if the test environment is used (only for 'profit' system)
|
|
59
|
+
:return: Credential dictionary or list of credential dictionaries for the specified system
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
# Fetch the config using a separate method
|
|
63
|
+
config = self._fetch_config()
|
|
64
|
+
|
|
65
|
+
matching_credentials = []
|
|
66
|
+
|
|
67
|
+
# If system_type is provided, only search in that list
|
|
68
|
+
if system_type:
|
|
69
|
+
if system_type not in ['source', 'target']:
|
|
70
|
+
raise ValueError("system_type must be either 'source' or 'target'")
|
|
71
|
+
credentials_list = config.get(f'{system_type}s', [])
|
|
72
|
+
for cred in credentials_list:
|
|
73
|
+
if cred.get('app') == system:
|
|
74
|
+
# Check test environment for 'profit'
|
|
75
|
+
if system == 'profit':
|
|
76
|
+
is_test = cred.get('data', {}).get('isTestEnvironment', False)
|
|
77
|
+
if is_test == test_environment:
|
|
78
|
+
matching_credentials.append({'credential': cred, 'type': system_type})
|
|
79
|
+
else:
|
|
80
|
+
matching_credentials.append({'credential': cred, 'type': system_type})
|
|
81
|
+
|
|
82
|
+
# If no system_type provided, search both lists
|
|
83
|
+
else:
|
|
84
|
+
source_credentials = []
|
|
85
|
+
target_credentials = []
|
|
86
|
+
|
|
87
|
+
# Check sources
|
|
88
|
+
for source in config.get('sources', []):
|
|
89
|
+
if source.get('app') == system:
|
|
90
|
+
if system == 'profit':
|
|
91
|
+
is_test = source.get('data', {}).get('isTestEnvironment', False)
|
|
92
|
+
if is_test == test_environment:
|
|
93
|
+
source_credentials.append({'credential': source, 'type': 'source'})
|
|
94
|
+
else:
|
|
95
|
+
source_credentials.append({'credential': source, 'type': 'source'})
|
|
96
|
+
|
|
97
|
+
# Check targets
|
|
98
|
+
for target in config.get('targets', []):
|
|
99
|
+
if target.get('app') == system:
|
|
100
|
+
if system == 'profit':
|
|
101
|
+
is_test = target.get('data', {}).get('isTestEnvironment', False)
|
|
102
|
+
if is_test == test_environment:
|
|
103
|
+
target_credentials.append({'credential': target, 'type': 'target'})
|
|
104
|
+
else:
|
|
105
|
+
target_credentials.append({'credential': target, 'type': 'target'})
|
|
106
|
+
|
|
107
|
+
# Combine matching credentials based on type
|
|
108
|
+
if source_credentials and target_credentials:
|
|
109
|
+
raise ValueError(
|
|
110
|
+
f'Multiple credentials found for system {system} in both source and target. '
|
|
111
|
+
f'Please specify system_type as "source" or "target"'
|
|
112
|
+
)
|
|
113
|
+
matching_credentials = source_credentials or target_credentials
|
|
114
|
+
|
|
115
|
+
# Handle results
|
|
116
|
+
if len(matching_credentials) == 0:
|
|
117
|
+
if system == 'profit':
|
|
118
|
+
raise ValueError(f'No credentials found for system {system} with test_environment={test_environment}')
|
|
119
|
+
else:
|
|
120
|
+
raise ValueError(f'No credentials found for system {system}')
|
|
121
|
+
|
|
122
|
+
if len(matching_credentials) == 1:
|
|
123
|
+
return matching_credentials[0]['credential']
|
|
124
|
+
|
|
125
|
+
if len(matching_credentials) > 1:
|
|
126
|
+
warning_msg = f'Multiple credentials found for system {system}'
|
|
127
|
+
if system_type:
|
|
128
|
+
warning_msg += f' in {system_type}'
|
|
129
|
+
warnings.warn(warning_msg)
|
|
130
|
+
return [cred['credential'] for cred in matching_credentials]
|
|
131
|
+
|
|
132
|
+
def _fetch_config(self) -> Dict[str, Any]:
|
|
133
|
+
"""
|
|
134
|
+
Fetch configuration from BrynQ for a given interface ID.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
interface_id (str): The ID of the interface to fetch configuration for.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Dict[str, Any]: Validated credentials configuration.
|
|
141
|
+
|
|
142
|
+
Raises:
|
|
143
|
+
ValueError: If the response data is invalid.
|
|
144
|
+
requests.exceptions.RequestException: If the API request fails.
|
|
145
|
+
"""
|
|
146
|
+
response = self._brynq.brynq_session.get(
|
|
147
|
+
url=f'{self._brynq.url}interfaces/{self._brynq.data_interface_id}/config/auth',
|
|
148
|
+
timeout=self._brynq.timeout
|
|
149
|
+
)
|
|
150
|
+
response.raise_for_status()
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
config_data = response.json()
|
|
154
|
+
valid_data, _ = Functions.validate_pydantic_data(config_data, CredentialsConfig, debug=False)
|
|
155
|
+
return valid_data[0]
|
|
156
|
+
except ValueError as e:
|
|
157
|
+
raise ValueError(f"Invalid credentials configuration received from API: {str(e)}")
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from typing import List, Dict, Any, Optional
|
|
2
|
+
import requests
|
|
3
|
+
from requests import Response
|
|
4
|
+
from .schemas.customers import CustomerSchema, CustomerContractDetailsSchema
|
|
5
|
+
from brynq_sdk_functions import Functions
|
|
6
|
+
|
|
7
|
+
class Customers:
|
|
8
|
+
"""Class for interacting with BrynQ customer endpoints"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, brynq):
|
|
11
|
+
"""Initialize Customers class
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
brynq: Parent BrynQ instance for authentication and configuration
|
|
15
|
+
"""
|
|
16
|
+
self.brynq = brynq
|
|
17
|
+
|
|
18
|
+
def get(self) -> List[Dict[str, Any]]:
|
|
19
|
+
"""Get all customers this token has access to.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
List[Dict[str, Any]]: List of customer objects with validated data
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
requests.exceptions.RequestException: If the API request fails
|
|
26
|
+
ValueError: If the response data doesn't match the expected schema
|
|
27
|
+
"""
|
|
28
|
+
response = self.brynq.brynq_session.get(
|
|
29
|
+
url=f"{self.brynq.url}customers",
|
|
30
|
+
timeout=self.brynq.timeout
|
|
31
|
+
)
|
|
32
|
+
response.raise_for_status()
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
customers_data = response.json()
|
|
36
|
+
valid_data, _ = Functions.validate_pydantic_data(customers_data, CustomerSchema, debug=False)
|
|
37
|
+
return valid_data
|
|
38
|
+
except ValueError as e:
|
|
39
|
+
raise ValueError(f"Invalid customer data received from API: {str(e)}")
|
|
40
|
+
|
|
41
|
+
def get_all_contract_details(self) -> List[Dict[str, Any]]:
|
|
42
|
+
"""Get all customers contract details.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
List[Dict[str, Any]]: List of customer contract details with validated data
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
requests.exceptions.RequestException: If the API request fails
|
|
49
|
+
ValueError: If the response data doesn't match the expected schema
|
|
50
|
+
"""
|
|
51
|
+
response = self.brynq.brynq_session.get(
|
|
52
|
+
f"{self.brynq.url}customers/contract-details",
|
|
53
|
+
timeout=self.brynq.timeout
|
|
54
|
+
)
|
|
55
|
+
response.raise_for_status()
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
contract_details = response.json()
|
|
59
|
+
valid_data, _ = Functions.validate_pydantic_data(contract_details, CustomerContractDetailsSchema, debug=False)
|
|
60
|
+
return valid_data
|
|
61
|
+
except ValueError as e:
|
|
62
|
+
raise ValueError(f"Invalid contract details received from API: {str(e)}")
|
|
63
|
+
|
|
64
|
+
def get_contract_details_by_id(self, customer_id: int) -> Dict[str, Any]:
|
|
65
|
+
"""Get contract details for a specific customer.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
customer_id (int): The ID of the customer to get contract details for.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Dict[str, Any]: Contract details for the specified customer.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
ValueError: If the response data is invalid.
|
|
75
|
+
requests.exceptions.RequestException: If the API request fails.
|
|
76
|
+
"""
|
|
77
|
+
response = self.brynq.brynq_session.get(
|
|
78
|
+
f"{self.brynq.url}customers/{customer_id}/contract-details",
|
|
79
|
+
timeout=self.brynq.timeout
|
|
80
|
+
)
|
|
81
|
+
response.raise_for_status()
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
contract_details = response.json()
|
|
85
|
+
valid_data, _ = Functions.validate_pydantic_data(contract_details, CustomerContractDetailsSchema, debug=False)
|
|
86
|
+
return valid_data[0]
|
|
87
|
+
except ValueError as e:
|
|
88
|
+
raise ValueError(f"Invalid contract details received from API: {str(e)}")
|