bb-integrations-library 3.0.11__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.
- bb_integrations_lib/__init__.py +0 -0
- bb_integrations_lib/converters/__init__.py +0 -0
- bb_integrations_lib/gravitate/__init__.py +0 -0
- bb_integrations_lib/gravitate/base_api.py +20 -0
- bb_integrations_lib/gravitate/model.py +29 -0
- bb_integrations_lib/gravitate/pe_api.py +122 -0
- bb_integrations_lib/gravitate/rita_api.py +552 -0
- bb_integrations_lib/gravitate/sd_api.py +572 -0
- bb_integrations_lib/gravitate/testing/TTE/sd/models.py +1398 -0
- bb_integrations_lib/gravitate/testing/TTE/sd/tests/test_models.py +2987 -0
- bb_integrations_lib/gravitate/testing/__init__.py +0 -0
- bb_integrations_lib/gravitate/testing/builder.py +55 -0
- bb_integrations_lib/gravitate/testing/openapi.py +70 -0
- bb_integrations_lib/gravitate/testing/util.py +274 -0
- bb_integrations_lib/mappers/__init__.py +0 -0
- bb_integrations_lib/mappers/prices/__init__.py +0 -0
- bb_integrations_lib/mappers/prices/model.py +106 -0
- bb_integrations_lib/mappers/prices/price_mapper.py +127 -0
- bb_integrations_lib/mappers/prices/protocol.py +20 -0
- bb_integrations_lib/mappers/prices/util.py +61 -0
- bb_integrations_lib/mappers/rita_mapper.py +523 -0
- bb_integrations_lib/models/__init__.py +0 -0
- bb_integrations_lib/models/dtn_supplier_invoice.py +487 -0
- bb_integrations_lib/models/enums.py +28 -0
- bb_integrations_lib/models/pipeline_structs.py +76 -0
- bb_integrations_lib/models/probe/probe_event.py +20 -0
- bb_integrations_lib/models/probe/request_data.py +431 -0
- bb_integrations_lib/models/probe/resume_token.py +7 -0
- bb_integrations_lib/models/rita/audit.py +113 -0
- bb_integrations_lib/models/rita/auth.py +30 -0
- bb_integrations_lib/models/rita/bucket.py +17 -0
- bb_integrations_lib/models/rita/config.py +188 -0
- bb_integrations_lib/models/rita/constants.py +19 -0
- bb_integrations_lib/models/rita/crossroads_entities.py +293 -0
- bb_integrations_lib/models/rita/crossroads_mapping.py +428 -0
- bb_integrations_lib/models/rita/crossroads_monitoring.py +78 -0
- bb_integrations_lib/models/rita/crossroads_network.py +41 -0
- bb_integrations_lib/models/rita/crossroads_rules.py +80 -0
- bb_integrations_lib/models/rita/email.py +39 -0
- bb_integrations_lib/models/rita/issue.py +63 -0
- bb_integrations_lib/models/rita/mapping.py +227 -0
- bb_integrations_lib/models/rita/probe.py +58 -0
- bb_integrations_lib/models/rita/reference_data.py +110 -0
- bb_integrations_lib/models/rita/source_system.py +9 -0
- bb_integrations_lib/models/rita/workers.py +76 -0
- bb_integrations_lib/models/sd/bols_and_drops.py +241 -0
- bb_integrations_lib/models/sd/get_order.py +301 -0
- bb_integrations_lib/models/sd/orders.py +18 -0
- bb_integrations_lib/models/sd_api.py +115 -0
- bb_integrations_lib/pipelines/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/distribution_report/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/distribution_report/order_by_site_product_parser.py +50 -0
- bb_integrations_lib/pipelines/parsers/distribution_report/tank_configs_parser.py +47 -0
- bb_integrations_lib/pipelines/parsers/dtn/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/dtn/dtn_price_parser.py +102 -0
- bb_integrations_lib/pipelines/parsers/dtn/model.py +79 -0
- bb_integrations_lib/pipelines/parsers/price_engine/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/price_engine/parse_accessorials_prices_parser.py +67 -0
- bb_integrations_lib/pipelines/parsers/price_engine/price_file_upload/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/price_engine/price_file_upload/price_merge_parser.py +111 -0
- bb_integrations_lib/pipelines/parsers/price_engine/price_file_upload/price_sync_parser.py +107 -0
- bb_integrations_lib/pipelines/parsers/price_engine/price_file_upload/shared.py +81 -0
- bb_integrations_lib/pipelines/parsers/tank_reading_parser.py +155 -0
- bb_integrations_lib/pipelines/parsers/tank_sales_parser.py +144 -0
- bb_integrations_lib/pipelines/shared/__init__.py +0 -0
- bb_integrations_lib/pipelines/shared/allocation_matching.py +227 -0
- bb_integrations_lib/pipelines/shared/bol_allocation.py +2793 -0
- bb_integrations_lib/pipelines/steps/__init__.py +0 -0
- bb_integrations_lib/pipelines/steps/create_accessorials_step.py +80 -0
- bb_integrations_lib/pipelines/steps/distribution_report/__init__.py +0 -0
- bb_integrations_lib/pipelines/steps/distribution_report/distribution_report_datafram_to_raw_data.py +33 -0
- bb_integrations_lib/pipelines/steps/distribution_report/get_model_history_step.py +50 -0
- bb_integrations_lib/pipelines/steps/distribution_report/get_order_by_site_product_step.py +62 -0
- bb_integrations_lib/pipelines/steps/distribution_report/get_tank_configs_step.py +40 -0
- bb_integrations_lib/pipelines/steps/distribution_report/join_distribution_order_dos_step.py +85 -0
- bb_integrations_lib/pipelines/steps/distribution_report/upload_distribution_report_datafram_to_big_query.py +47 -0
- bb_integrations_lib/pipelines/steps/echo_step.py +14 -0
- bb_integrations_lib/pipelines/steps/export_dataframe_to_rawdata_step.py +28 -0
- bb_integrations_lib/pipelines/steps/exporting/__init__.py +0 -0
- bb_integrations_lib/pipelines/steps/exporting/bbd_export_payroll_file_step.py +107 -0
- bb_integrations_lib/pipelines/steps/exporting/bbd_export_readings_step.py +236 -0
- bb_integrations_lib/pipelines/steps/exporting/cargas_wholesale_bundle_upload_step.py +33 -0
- bb_integrations_lib/pipelines/steps/exporting/dataframe_flat_file_export.py +29 -0
- bb_integrations_lib/pipelines/steps/exporting/gcs_bucket_export_file_step.py +34 -0
- bb_integrations_lib/pipelines/steps/exporting/keyvu_export_step.py +356 -0
- bb_integrations_lib/pipelines/steps/exporting/pe_price_export_step.py +238 -0
- bb_integrations_lib/pipelines/steps/exporting/platform_science_order_sync_step.py +500 -0
- bb_integrations_lib/pipelines/steps/exporting/save_rawdata_to_disk.py +15 -0
- bb_integrations_lib/pipelines/steps/exporting/sftp_export_file_step.py +60 -0
- bb_integrations_lib/pipelines/steps/exporting/sftp_export_many_files_step.py +23 -0
- bb_integrations_lib/pipelines/steps/exporting/update_exported_orders_table_step.py +64 -0
- bb_integrations_lib/pipelines/steps/filter_step.py +22 -0
- bb_integrations_lib/pipelines/steps/get_latest_sync_date.py +34 -0
- bb_integrations_lib/pipelines/steps/importing/bbd_import_payroll_step.py +30 -0
- bb_integrations_lib/pipelines/steps/importing/get_order_numbers_to_export_step.py +138 -0
- bb_integrations_lib/pipelines/steps/importing/load_file_to_dataframe_step.py +46 -0
- bb_integrations_lib/pipelines/steps/importing/load_imap_attachment_step.py +172 -0
- bb_integrations_lib/pipelines/steps/importing/pe_bulk_sync_price_structure_step.py +68 -0
- bb_integrations_lib/pipelines/steps/importing/pe_price_merge_step.py +86 -0
- bb_integrations_lib/pipelines/steps/importing/sftp_file_config_step.py +124 -0
- bb_integrations_lib/pipelines/steps/importing/test_exact_file_match.py +57 -0
- bb_integrations_lib/pipelines/steps/null_step.py +15 -0
- bb_integrations_lib/pipelines/steps/pe_integration_job_step.py +32 -0
- bb_integrations_lib/pipelines/steps/processing/__init__.py +0 -0
- bb_integrations_lib/pipelines/steps/processing/archive_gcs_step.py +76 -0
- bb_integrations_lib/pipelines/steps/processing/archive_sftp_step.py +48 -0
- bb_integrations_lib/pipelines/steps/processing/bbd_format_tank_readings_step.py +492 -0
- bb_integrations_lib/pipelines/steps/processing/bbd_upload_prices_step.py +54 -0
- bb_integrations_lib/pipelines/steps/processing/bbd_upload_tank_sales_step.py +124 -0
- bb_integrations_lib/pipelines/steps/processing/bbd_upload_tankreading_step.py +80 -0
- bb_integrations_lib/pipelines/steps/processing/convert_bbd_order_to_cargas_step.py +226 -0
- bb_integrations_lib/pipelines/steps/processing/delete_sftp_step.py +33 -0
- bb_integrations_lib/pipelines/steps/processing/dtn/__init__.py +2 -0
- bb_integrations_lib/pipelines/steps/processing/dtn/convert_dtn_invoice_to_sd_model.py +145 -0
- bb_integrations_lib/pipelines/steps/processing/dtn/parse_dtn_invoice_step.py +38 -0
- bb_integrations_lib/pipelines/steps/processing/file_config_parser_step.py +720 -0
- bb_integrations_lib/pipelines/steps/processing/file_config_parser_step_v2.py +418 -0
- bb_integrations_lib/pipelines/steps/processing/get_sd_price_price_request.py +105 -0
- bb_integrations_lib/pipelines/steps/processing/keyvu_upload_deliveryplan_step.py +39 -0
- bb_integrations_lib/pipelines/steps/processing/mark_orders_exported_in_bbd_step.py +185 -0
- bb_integrations_lib/pipelines/steps/processing/pe_price_rows_processing_step.py +174 -0
- bb_integrations_lib/pipelines/steps/processing/send_process_report_step.py +47 -0
- bb_integrations_lib/pipelines/steps/processing/sftp_renamer_step.py +61 -0
- bb_integrations_lib/pipelines/steps/processing/tank_reading_touchup_steps.py +75 -0
- bb_integrations_lib/pipelines/steps/processing/upload_supplier_invoice_step.py +16 -0
- bb_integrations_lib/pipelines/steps/send_attached_in_rita_email_step.py +44 -0
- bb_integrations_lib/pipelines/steps/send_rita_email_step.py +34 -0
- bb_integrations_lib/pipelines/steps/sleep_step.py +24 -0
- bb_integrations_lib/pipelines/wrappers/__init__.py +0 -0
- bb_integrations_lib/pipelines/wrappers/accessorials_transformation.py +104 -0
- bb_integrations_lib/pipelines/wrappers/distribution_report.py +191 -0
- bb_integrations_lib/pipelines/wrappers/export_tank_readings.py +237 -0
- bb_integrations_lib/pipelines/wrappers/import_tank_readings.py +192 -0
- bb_integrations_lib/pipelines/wrappers/wrapper.py +81 -0
- bb_integrations_lib/protocols/__init__.py +0 -0
- bb_integrations_lib/protocols/flat_file.py +210 -0
- bb_integrations_lib/protocols/gravitate_client.py +104 -0
- bb_integrations_lib/protocols/pipelines.py +697 -0
- bb_integrations_lib/provider/__init__.py +0 -0
- bb_integrations_lib/provider/api/__init__.py +0 -0
- bb_integrations_lib/provider/api/cargas/__init__.py +0 -0
- bb_integrations_lib/provider/api/cargas/client.py +43 -0
- bb_integrations_lib/provider/api/cargas/model.py +49 -0
- bb_integrations_lib/provider/api/cargas/protocol.py +23 -0
- bb_integrations_lib/provider/api/dtn/__init__.py +0 -0
- bb_integrations_lib/provider/api/dtn/client.py +128 -0
- bb_integrations_lib/provider/api/dtn/protocol.py +9 -0
- bb_integrations_lib/provider/api/keyvu/__init__.py +0 -0
- bb_integrations_lib/provider/api/keyvu/client.py +30 -0
- bb_integrations_lib/provider/api/keyvu/model.py +149 -0
- bb_integrations_lib/provider/api/macropoint/__init__.py +0 -0
- bb_integrations_lib/provider/api/macropoint/client.py +28 -0
- bb_integrations_lib/provider/api/macropoint/model.py +40 -0
- bb_integrations_lib/provider/api/pc_miler/__init__.py +0 -0
- bb_integrations_lib/provider/api/pc_miler/client.py +130 -0
- bb_integrations_lib/provider/api/pc_miler/model.py +6 -0
- bb_integrations_lib/provider/api/pc_miler/web_services_apis.py +131 -0
- bb_integrations_lib/provider/api/platform_science/__init__.py +0 -0
- bb_integrations_lib/provider/api/platform_science/client.py +147 -0
- bb_integrations_lib/provider/api/platform_science/model.py +82 -0
- bb_integrations_lib/provider/api/quicktrip/__init__.py +0 -0
- bb_integrations_lib/provider/api/quicktrip/client.py +52 -0
- bb_integrations_lib/provider/api/telapoint/__init__.py +0 -0
- bb_integrations_lib/provider/api/telapoint/client.py +68 -0
- bb_integrations_lib/provider/api/telapoint/model.py +178 -0
- bb_integrations_lib/provider/api/warren_rogers/__init__.py +0 -0
- bb_integrations_lib/provider/api/warren_rogers/client.py +207 -0
- bb_integrations_lib/provider/aws/__init__.py +0 -0
- bb_integrations_lib/provider/aws/s3/__init__.py +0 -0
- bb_integrations_lib/provider/aws/s3/client.py +126 -0
- bb_integrations_lib/provider/ftp/__init__.py +0 -0
- bb_integrations_lib/provider/ftp/client.py +140 -0
- bb_integrations_lib/provider/ftp/interface.py +273 -0
- bb_integrations_lib/provider/ftp/model.py +76 -0
- bb_integrations_lib/provider/imap/__init__.py +0 -0
- bb_integrations_lib/provider/imap/client.py +228 -0
- bb_integrations_lib/provider/imap/model.py +3 -0
- bb_integrations_lib/provider/sqlserver/__init__.py +0 -0
- bb_integrations_lib/provider/sqlserver/client.py +106 -0
- bb_integrations_lib/secrets/__init__.py +4 -0
- bb_integrations_lib/secrets/adapters.py +98 -0
- bb_integrations_lib/secrets/credential_models.py +222 -0
- bb_integrations_lib/secrets/factory.py +85 -0
- bb_integrations_lib/secrets/providers.py +160 -0
- bb_integrations_lib/shared/__init__.py +0 -0
- bb_integrations_lib/shared/exceptions.py +25 -0
- bb_integrations_lib/shared/model.py +1039 -0
- bb_integrations_lib/shared/shared_enums.py +510 -0
- bb_integrations_lib/storage/README.md +236 -0
- bb_integrations_lib/storage/__init__.py +0 -0
- bb_integrations_lib/storage/aws/__init__.py +0 -0
- bb_integrations_lib/storage/aws/s3.py +8 -0
- bb_integrations_lib/storage/defaults.py +72 -0
- bb_integrations_lib/storage/gcs/__init__.py +0 -0
- bb_integrations_lib/storage/gcs/client.py +8 -0
- bb_integrations_lib/storage/gcsmanager/__init__.py +0 -0
- bb_integrations_lib/storage/gcsmanager/client.py +8 -0
- bb_integrations_lib/storage/setup.py +29 -0
- bb_integrations_lib/util/__init__.py +0 -0
- bb_integrations_lib/util/cache/__init__.py +0 -0
- bb_integrations_lib/util/cache/custom_ttl_cache.py +75 -0
- bb_integrations_lib/util/cache/protocol.py +9 -0
- bb_integrations_lib/util/config/__init__.py +0 -0
- bb_integrations_lib/util/config/manager.py +391 -0
- bb_integrations_lib/util/config/model.py +41 -0
- bb_integrations_lib/util/exception_logger/__init__.py +0 -0
- bb_integrations_lib/util/exception_logger/exception_logger.py +146 -0
- bb_integrations_lib/util/exception_logger/test.py +114 -0
- bb_integrations_lib/util/utils.py +364 -0
- bb_integrations_lib/workers/__init__.py +0 -0
- bb_integrations_lib/workers/groups.py +13 -0
- bb_integrations_lib/workers/rpc_worker.py +50 -0
- bb_integrations_lib/workers/topics.py +20 -0
- bb_integrations_library-3.0.11.dist-info/METADATA +59 -0
- bb_integrations_library-3.0.11.dist-info/RECORD +217 -0
- bb_integrations_library-3.0.11.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pprint
|
|
3
|
+
from json import JSONDecodeError
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from async_lru import alru_cache
|
|
7
|
+
from httpx import HTTPStatusError
|
|
8
|
+
from requests import Response
|
|
9
|
+
|
|
10
|
+
from bb_integrations_lib.models.rita.bucket import Bucket
|
|
11
|
+
from bb_integrations_lib.models.rita.crossroads_mapping import CrossroadsMapping, CrossroadsMappingResult, \
|
|
12
|
+
MappingRequest
|
|
13
|
+
from bb_integrations_lib.models.rita.crossroads_monitoring import CrossroadsMappingError, CrossroadsError
|
|
14
|
+
from bb_integrations_lib.models.rita.reference_data import ReferenceDataMapping
|
|
15
|
+
from typing import Optional, Dict, Union, List, runtime_checkable, Protocol, Self
|
|
16
|
+
|
|
17
|
+
import loguru
|
|
18
|
+
from pydantic import ValidationError, BaseModel
|
|
19
|
+
|
|
20
|
+
from bb_integrations_lib.gravitate.base_api import BaseAPI
|
|
21
|
+
from bb_integrations_lib.secrets import RITACredential
|
|
22
|
+
from bb_integrations_lib.util.utils import CustomJSONEncoder
|
|
23
|
+
from bb_integrations_lib.models.probe.resume_token import ResumeToken
|
|
24
|
+
from bb_integrations_lib.models.rita.audit import CreateReportV2, ProcessReportBaseV2, UpdateReportV2
|
|
25
|
+
from bb_integrations_lib.models.rita.config import FileConfig, Config, GenericConfig, ConfigType, MaxSync
|
|
26
|
+
from bb_integrations_lib.models.rita.email import EmailData
|
|
27
|
+
from bb_integrations_lib.models.rita.issue import IssueBase, UpdateIssue
|
|
28
|
+
from bb_integrations_lib.models.rita.mapping import Map
|
|
29
|
+
from bb_integrations_lib.models.rita.probe import ProbeConfig
|
|
30
|
+
from bb_integrations_lib.protocols.flat_file import TankReading
|
|
31
|
+
from bb_integrations_lib.shared.model import CreateProcess
|
|
32
|
+
from bb_integrations_lib.util.config.model import Config as GConfig
|
|
33
|
+
|
|
34
|
+
def catch_exceptions(func):
|
|
35
|
+
def wrapper(*args, **kwargs):
|
|
36
|
+
try:
|
|
37
|
+
return func(*args, **kwargs)
|
|
38
|
+
except httpx.HTTPStatusError as e:
|
|
39
|
+
loguru.logger.error(f"HTTP status error: {e}")
|
|
40
|
+
raise e
|
|
41
|
+
except httpx.ConnectError as e:
|
|
42
|
+
loguru.logger.error(f"Connection error: {e}")
|
|
43
|
+
raise e
|
|
44
|
+
except httpx.ReadTimeout as e:
|
|
45
|
+
loguru.logger.error(f"Read timeout: {e}")
|
|
46
|
+
raise e
|
|
47
|
+
except Exception as e:
|
|
48
|
+
loguru.logger.error(f"Unexpected error: {e}")
|
|
49
|
+
raise e
|
|
50
|
+
return wrapper
|
|
51
|
+
|
|
52
|
+
@runtime_checkable
|
|
53
|
+
class RitaBackendAPI(Protocol):
|
|
54
|
+
async def get_mappings(self, source_system: Optional[str] = None, mapping_type: Optional[str] = None) -> list[Map]:
|
|
55
|
+
"""Gets all mappings by source_system and mapping type from RITA"""
|
|
56
|
+
|
|
57
|
+
async def get_process_reports_sync(self, config_id: str) -> dict:
|
|
58
|
+
"""Gets latest sync date for a specific config"""
|
|
59
|
+
|
|
60
|
+
async def get_file_configs(self, name: Optional[str] = None) -> dict[str, FileConfig]:
|
|
61
|
+
"""Gets File Configs from RITA"""
|
|
62
|
+
|
|
63
|
+
async def create_process_report(self, new_process: CreateReportV2) -> ProcessReportBaseV2:
|
|
64
|
+
"""Creates a new process report in RITA"""
|
|
65
|
+
|
|
66
|
+
async def get_config_by_id(self, id: str) -> Config:
|
|
67
|
+
"""Get a config by its id"""
|
|
68
|
+
|
|
69
|
+
async def get_config_by_name(self, bucket_path: str, config_name: str) -> Dict[str, Union[FileConfig, ProbeConfig, GenericConfig]]:
|
|
70
|
+
"""Gets a config by its name"""
|
|
71
|
+
|
|
72
|
+
class GetMappings(BaseModel):
|
|
73
|
+
source_system: Optional[str] = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class GetConfigs(BaseModel):
|
|
77
|
+
name: Optional[str] = None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class GravitateRitaAPI(BaseAPI):
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
client_id: str | None = None,
|
|
84
|
+
client_secret: str | None = None,
|
|
85
|
+
username: str | None = None,
|
|
86
|
+
password: str | None = None,
|
|
87
|
+
tenant: str | None = None,
|
|
88
|
+
raise_errors: bool = True,
|
|
89
|
+
base_url: str = "https://rita.gravitate.energy/api/"
|
|
90
|
+
):
|
|
91
|
+
super().__init__(raise_errors)
|
|
92
|
+
self.base_url = base_url
|
|
93
|
+
self.client_id = client_id
|
|
94
|
+
self.client_secret = client_secret
|
|
95
|
+
self.username = username
|
|
96
|
+
self.password = password
|
|
97
|
+
self.tenant = tenant
|
|
98
|
+
self._token = None
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def from_global_config(cls, c: GConfig, tenant: str):
|
|
102
|
+
return cls(client_id=c.client_id, client_secret=c.client_secret, username=c.username, password=c.password, base_url=c.base_url, tenant=tenant)
|
|
103
|
+
|
|
104
|
+
async def _get_token(self):
|
|
105
|
+
try:
|
|
106
|
+
if self.username and self.password:
|
|
107
|
+
resp = await self.post(
|
|
108
|
+
url=f"{self.base_url}auth/token",
|
|
109
|
+
data={"username": self.username, "password": self.password},
|
|
110
|
+
)
|
|
111
|
+
elif self.client_id and self.client_secret:
|
|
112
|
+
resp = await self.post(
|
|
113
|
+
url=f"{self.base_url}auth/token",
|
|
114
|
+
data={"client_id": self.client_id, "client_secret": self.client_secret},
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
raise RuntimeError("Missing credentials for token request")
|
|
118
|
+
except Exception:
|
|
119
|
+
raise ValueError(f"Error Getting Token for {self.base_url}")
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
self._token = resp.json()["access_token"]
|
|
123
|
+
return self._token
|
|
124
|
+
except Exception:
|
|
125
|
+
raise ValueError(f"Could Not Get Token for {self.base_url} -> {resp.status_code}")
|
|
126
|
+
|
|
127
|
+
async def _auth_req(self, method="POST", **kwargs):
|
|
128
|
+
if not self._token:
|
|
129
|
+
await self._get_token()
|
|
130
|
+
|
|
131
|
+
headers = kwargs.pop("headers", {})
|
|
132
|
+
headers["authorization"] = f"Bearer {self._token}"
|
|
133
|
+
headers["X-Tenant-Name"] = self.tenant
|
|
134
|
+
kwargs["headers"] = headers
|
|
135
|
+
kwargs["url"] = f"{self.base_url}{kwargs.get("url", "")}"
|
|
136
|
+
|
|
137
|
+
resp = await self.request(method, **kwargs)
|
|
138
|
+
|
|
139
|
+
if resp.status_code == 401:
|
|
140
|
+
await self._get_token()
|
|
141
|
+
headers["authorization"] = f"Bearer {self._token}"
|
|
142
|
+
kwargs["headers"] = headers
|
|
143
|
+
resp = await self.request(method, **kwargs)
|
|
144
|
+
|
|
145
|
+
if resp.status_code == 422:
|
|
146
|
+
try:
|
|
147
|
+
resp_content = pprint.pformat(resp.json())
|
|
148
|
+
except JSONDecodeError:
|
|
149
|
+
resp_content = resp.text
|
|
150
|
+
raise HTTPStatusError(
|
|
151
|
+
f"Bad request: \n{resp_content}",
|
|
152
|
+
request=resp.request,
|
|
153
|
+
response=resp
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return resp
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def from_credential(cls, credential: RITACredential) -> Self:
|
|
160
|
+
return cls(
|
|
161
|
+
base_url=credential.base_url,
|
|
162
|
+
username=credential.username,
|
|
163
|
+
password=credential.password,
|
|
164
|
+
client_id=credential.client_id,
|
|
165
|
+
client_secret=credential.client_secret,
|
|
166
|
+
tenant=credential.tenant,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
async def token_post(self, **kwargs):
|
|
170
|
+
return await self._auth_req("POST", **kwargs)
|
|
171
|
+
|
|
172
|
+
async def token_get(self, **kwargs):
|
|
173
|
+
return await self._auth_req("GET", **kwargs)
|
|
174
|
+
|
|
175
|
+
@catch_exceptions
|
|
176
|
+
@alru_cache(maxsize=128)
|
|
177
|
+
async def get_file_configs(self, name: Optional[str] = None) -> dict[str, FileConfig]:
|
|
178
|
+
try:
|
|
179
|
+
_data = (GetConfigs(name=name).model_dump() if name else {})
|
|
180
|
+
resp = await self.token_post(url="config/all", json=_data)
|
|
181
|
+
resp.raise_for_status()
|
|
182
|
+
return self.build_fileconfig_dict(resp.json())
|
|
183
|
+
except Exception as e:
|
|
184
|
+
loguru.logger.error(f"Error while fetching configurations: {e}")
|
|
185
|
+
raise e
|
|
186
|
+
|
|
187
|
+
@catch_exceptions
|
|
188
|
+
@alru_cache(maxsize=128)
|
|
189
|
+
async def get_config_by_name(self, bucket_path: str, config_name: str) -> Dict[str, Union[FileConfig, ProbeConfig, Dict]]:
|
|
190
|
+
try:
|
|
191
|
+
data = {"bucket_path": bucket_path, "name": config_name,
|
|
192
|
+
"bucket_name": bucket_path} # Some older versions of RITA use bucket_name instead of bucket_path
|
|
193
|
+
resp = await self.token_post(url="config/by_name", json=data)
|
|
194
|
+
resp.raise_for_status()
|
|
195
|
+
return self.build_config_dict([resp.json()])
|
|
196
|
+
except Exception as e:
|
|
197
|
+
loguru.logger.error(f"Error while fetching configs from bucket: {e}")
|
|
198
|
+
raise e
|
|
199
|
+
|
|
200
|
+
@catch_exceptions
|
|
201
|
+
@alru_cache(maxsize=128)
|
|
202
|
+
async def get_all_buckets(self) -> list[dict]:
|
|
203
|
+
resp = await self.token_post(url="bucket/all")
|
|
204
|
+
resp.raise_for_status()
|
|
205
|
+
return resp.json()
|
|
206
|
+
|
|
207
|
+
@catch_exceptions
|
|
208
|
+
@alru_cache(maxsize=128)
|
|
209
|
+
async def get_mappings(self, source_system: Optional[str] = None, mapping_type: Optional[str] = None) -> list[Map]:
|
|
210
|
+
try:
|
|
211
|
+
_data = (GetMappings(source_system=source_system).model_dump() if source_system else {})
|
|
212
|
+
resp = await self.token_post(url="integration/mappings/all", json=_data)
|
|
213
|
+
resp.raise_for_status()
|
|
214
|
+
mappings: List[Map] = [Map.model_validate(obj) for obj in resp.json()]
|
|
215
|
+
if mapping_type:
|
|
216
|
+
mappings = list(filter(lambda m: m.type == mapping_type, mappings))
|
|
217
|
+
return mappings
|
|
218
|
+
except Exception as e:
|
|
219
|
+
loguru.logger.error(f"Error while fetching mappings: {e}")
|
|
220
|
+
raise e
|
|
221
|
+
|
|
222
|
+
@catch_exceptions
|
|
223
|
+
@alru_cache(maxsize=128)
|
|
224
|
+
async def get_mappings_by_source_system(self, source_system: str) -> list[Map]:
|
|
225
|
+
def is_valid(obj) -> bool:
|
|
226
|
+
try:
|
|
227
|
+
map = Map.model_validate(obj)
|
|
228
|
+
return map.is_active
|
|
229
|
+
except ValidationError:
|
|
230
|
+
return False
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
resp = await self.token_post(url="mapping/by_source_system/", params={"source_system": source_system})
|
|
234
|
+
resp.raise_for_status()
|
|
235
|
+
mappings = [Map.model_validate(obj) for obj in resp.json() if is_valid(obj)]
|
|
236
|
+
return mappings
|
|
237
|
+
except Exception as e:
|
|
238
|
+
loguru.logger.error(f"Error while fetching mappings: {e}")
|
|
239
|
+
raise e
|
|
240
|
+
|
|
241
|
+
@catch_exceptions
|
|
242
|
+
@alru_cache(maxsize=128)
|
|
243
|
+
async def get_process_reports_sync(self, config_id: str, status: str = None):
|
|
244
|
+
try:
|
|
245
|
+
data = {
|
|
246
|
+
"config_id": config_id,
|
|
247
|
+
"status": status
|
|
248
|
+
}
|
|
249
|
+
resp = await self.token_post(url="process_report/latest_sync", params=data)
|
|
250
|
+
resp.raise_for_status()
|
|
251
|
+
return resp.json()
|
|
252
|
+
except Exception as e:
|
|
253
|
+
loguru.logger.error(f"Error while fetching process reports sync date: {e}")
|
|
254
|
+
raise e
|
|
255
|
+
|
|
256
|
+
@catch_exceptions
|
|
257
|
+
@alru_cache(maxsize=128)
|
|
258
|
+
async def get_mappings_by_bucket_path(self, bucket_path: str) -> list[Map]:
|
|
259
|
+
try:
|
|
260
|
+
resp = await self.token_post(url="mapping/by_bucket_path", json=bucket_path)
|
|
261
|
+
resp.raise_for_status()
|
|
262
|
+
return [Map.model_validate(obj) for obj in resp.json()]
|
|
263
|
+
except Exception as e:
|
|
264
|
+
loguru.logger.error(f"Error while fetching mappings: {e}")
|
|
265
|
+
raise e
|
|
266
|
+
|
|
267
|
+
@catch_exceptions
|
|
268
|
+
@alru_cache(maxsize=128)
|
|
269
|
+
async def get_fileconfigs_from_bucket(self, bucket_path: str) -> dict[str, FileConfig]:
|
|
270
|
+
try:
|
|
271
|
+
data = {"bucket_path": bucket_path}
|
|
272
|
+
resp = await self.token_post(url="config/from_bucket", json=data)
|
|
273
|
+
resp.raise_for_status()
|
|
274
|
+
return self.build_fileconfig_dict(resp.json())
|
|
275
|
+
except Exception as e:
|
|
276
|
+
loguru.logger.error(f"Error while fetching configs from bucket: {e}")
|
|
277
|
+
raise e
|
|
278
|
+
|
|
279
|
+
@catch_exceptions
|
|
280
|
+
@alru_cache(maxsize=128)
|
|
281
|
+
async def get_config_by_id(self, id: str) -> Config:
|
|
282
|
+
try:
|
|
283
|
+
resp = await self.token_post(url=f"config/{id}")
|
|
284
|
+
resp.raise_for_status()
|
|
285
|
+
return Config.model_validate(resp.json())
|
|
286
|
+
except Exception as e:
|
|
287
|
+
loguru.logger.error(f"Error while fetching config: {e}")
|
|
288
|
+
raise e
|
|
289
|
+
|
|
290
|
+
@catch_exceptions
|
|
291
|
+
@alru_cache(maxsize=128)
|
|
292
|
+
async def get_fileconfig_by_name(self, bucket_path: str, config_name: str) -> dict[str, FileConfig]:
|
|
293
|
+
try:
|
|
294
|
+
data = {"bucket_path": bucket_path, "name": config_name,
|
|
295
|
+
"bucket_name": bucket_path} # Some older versions of RITA use bucket_name instead of bucket_path
|
|
296
|
+
resp = await self.token_post(url="config/by_name", json=data)
|
|
297
|
+
resp.raise_for_status()
|
|
298
|
+
return self.build_fileconfig_dict([resp.json()])
|
|
299
|
+
except Exception as e:
|
|
300
|
+
loguru.logger.error(f"Error while fetching configs from bucket: {e}")
|
|
301
|
+
raise e
|
|
302
|
+
|
|
303
|
+
@catch_exceptions
|
|
304
|
+
@alru_cache(maxsize=128)
|
|
305
|
+
async def get_probeconfig_by_name(self, bucket_path: str, config_name: str) -> dict[str, ProbeConfig]:
|
|
306
|
+
try:
|
|
307
|
+
data = {"bucket_path": bucket_path, "name": config_name}
|
|
308
|
+
resp = await self.token_post(url="config/by_name", json=data)
|
|
309
|
+
resp.raise_for_status()
|
|
310
|
+
configs = [Config.model_validate(resp.json())]
|
|
311
|
+
return self.build_probeconfig_dict(configs)
|
|
312
|
+
except Exception as e:
|
|
313
|
+
loguru.logger.error(f"Error while fetching config '{config_name}' from bucket '{bucket_path}': {e}")
|
|
314
|
+
raise e
|
|
315
|
+
|
|
316
|
+
@catch_exceptions
|
|
317
|
+
async def get_config_max_sync(self, config_id: str) -> Response:
|
|
318
|
+
try:
|
|
319
|
+
resp = await self.token_post(url="config/max_sync", params={"config_id": config_id})
|
|
320
|
+
resp.raise_for_status()
|
|
321
|
+
return resp
|
|
322
|
+
except Exception as e:
|
|
323
|
+
loguru.logger.error(f"Error while fetching configurations: {e}")
|
|
324
|
+
raise e
|
|
325
|
+
|
|
326
|
+
@catch_exceptions
|
|
327
|
+
async def update_config_max_sync(self, config_id: str, max_sync: MaxSync) -> Response:
|
|
328
|
+
try:
|
|
329
|
+
resp = await self.token_post(url="config/update_max_sync", params={"config_id": config_id}, json=max_sync.model_dump(mode="json"))
|
|
330
|
+
resp.raise_for_status()
|
|
331
|
+
return resp
|
|
332
|
+
except Exception as e:
|
|
333
|
+
loguru.logger.error(f"Error while fetching configurations: {e}")
|
|
334
|
+
raise e
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@catch_exceptions
|
|
338
|
+
async def get_resume_token(self, probe_id: str) -> ResumeToken | None:
|
|
339
|
+
try:
|
|
340
|
+
resp = await self.token_post(url=f"probe/get_resume_token/{probe_id}")
|
|
341
|
+
resp.raise_for_status()
|
|
342
|
+
resp = resp.json()
|
|
343
|
+
if resp is not None:
|
|
344
|
+
return ResumeToken.model_validate(resp)
|
|
345
|
+
return None
|
|
346
|
+
except Exception as e:
|
|
347
|
+
loguru.logger.error(f"Error while fetching resume token: {e}")
|
|
348
|
+
raise e
|
|
349
|
+
|
|
350
|
+
@catch_exceptions
|
|
351
|
+
async def get_available_tenants(self):
|
|
352
|
+
try:
|
|
353
|
+
resp = await self.token_post(url="/meta/tenant/available")
|
|
354
|
+
resp.raise_for_status()
|
|
355
|
+
return resp.json()
|
|
356
|
+
except Exception as e:
|
|
357
|
+
loguru.logger.error(f"Error while fetching available tenants: {e}")
|
|
358
|
+
raise e
|
|
359
|
+
|
|
360
|
+
@catch_exceptions
|
|
361
|
+
async def set_resume_token(self, resume_token: ResumeToken):
|
|
362
|
+
try:
|
|
363
|
+
resp = await self.token_post(url=f"probe/set_resume_token", json=resume_token.model_dump())
|
|
364
|
+
resp.raise_for_status()
|
|
365
|
+
return resp.json()
|
|
366
|
+
except Exception as e:
|
|
367
|
+
loguru.logger.error(f"Error while setting resume token: {e}, resp={resp} {resp.content()}")
|
|
368
|
+
raise e
|
|
369
|
+
|
|
370
|
+
async def get_estick_wr(self, store_number: str, tank_number: str) -> TankReading:
|
|
371
|
+
try:
|
|
372
|
+
resp = await self.token_post(url="estick/wr", params={"store_number": store_number, "tank_id": tank_number})
|
|
373
|
+
resp.raise_for_status()
|
|
374
|
+
return TankReading(**resp.json())
|
|
375
|
+
except Exception as e:
|
|
376
|
+
loguru.logger.error(f"Error while fetching Warren Rogers estick reading: {e}")
|
|
377
|
+
raise e
|
|
378
|
+
|
|
379
|
+
async def get_estick_wr_many(self, store_number: str, tank_numbers: list[str]) -> list[TankReading]:
|
|
380
|
+
try:
|
|
381
|
+
resp = await self.token_post(url="estick/wr/many",
|
|
382
|
+
json={"store_number": store_number, "tank_ids": tank_numbers})
|
|
383
|
+
resp.raise_for_status()
|
|
384
|
+
return [TankReading(**reading) for reading in resp.json()]
|
|
385
|
+
except Exception as e:
|
|
386
|
+
loguru.logger.error(f"Error while fetching multiple Warren Rogers estick readings: {e}")
|
|
387
|
+
raise e
|
|
388
|
+
|
|
389
|
+
@catch_exceptions
|
|
390
|
+
async def create_process_report(self, new_process: CreateReportV2) -> ProcessReportBaseV2:
|
|
391
|
+
try:
|
|
392
|
+
_data = new_process.model_dump(exclude_none=True)
|
|
393
|
+
payload_json = json.dumps(_data, cls=CustomJSONEncoder)
|
|
394
|
+
# 30s timeout - large files may take a long time to upload
|
|
395
|
+
resp = await self.token_post(url="process_report/create", json=json.loads(payload_json), timeout=30.0)
|
|
396
|
+
resp.raise_for_status()
|
|
397
|
+
return ProcessReportBaseV2(**resp.json())
|
|
398
|
+
except Exception as e:
|
|
399
|
+
loguru.logger.error(f"Error while creating process_report: {e}")
|
|
400
|
+
raise e
|
|
401
|
+
|
|
402
|
+
async def update_process_report(self, update_process: UpdateReportV2) -> ProcessReportBaseV2:
|
|
403
|
+
try:
|
|
404
|
+
_data = update_process.model_dump(exclude_none=True)
|
|
405
|
+
payload_json = json.dumps(_data, cls=CustomJSONEncoder)
|
|
406
|
+
resp = await self.token_post(url="process_report/update", json=json.loads(payload_json))
|
|
407
|
+
resp.raise_for_status()
|
|
408
|
+
return ProcessReportBaseV2(**resp.json())
|
|
409
|
+
except Exception as e:
|
|
410
|
+
loguru.logger.error(f"Error while creating process_report: {e}")
|
|
411
|
+
raise e
|
|
412
|
+
|
|
413
|
+
async def record_many_issues(self, issues: List[IssueBase]):
|
|
414
|
+
try:
|
|
415
|
+
res = await self.token_post(url="issue/record_many", json=[issue.model_dump(mode="json") for issue in issues])
|
|
416
|
+
res.raise_for_status()
|
|
417
|
+
# Endpoint does not return anything other than the status code
|
|
418
|
+
except Exception as e:
|
|
419
|
+
loguru.logger.error(f"Error while recording issue(s): {e}")
|
|
420
|
+
raise e
|
|
421
|
+
|
|
422
|
+
async def record_issue(self, issue: IssueBase):
|
|
423
|
+
await self.record_many_issues([issue])
|
|
424
|
+
|
|
425
|
+
async def update_issue(self, issue: UpdateIssue) -> IssueBase:
|
|
426
|
+
try:
|
|
427
|
+
resp = await self.token_post(url="issue/update", content=issue.model_dump_json(exclude_unset=True))
|
|
428
|
+
resp.raise_for_status()
|
|
429
|
+
return IssueBase(**resp.json())
|
|
430
|
+
except Exception as e:
|
|
431
|
+
loguru.logger.error(f"Error while updating issue {issue.key}: {e}")
|
|
432
|
+
raise e
|
|
433
|
+
|
|
434
|
+
async def get_issue(self, key: str) -> IssueBase:
|
|
435
|
+
try:
|
|
436
|
+
resp = await self.token_post(url=f"issue/get/{key}")
|
|
437
|
+
resp.raise_for_status()
|
|
438
|
+
return IssueBase(**resp.json())
|
|
439
|
+
except Exception as e:
|
|
440
|
+
loguru.logger.error(f"Error while updating issue {key}: {e}")
|
|
441
|
+
raise e
|
|
442
|
+
|
|
443
|
+
async def get_issues_by_config_id(self, config_id: str) -> list[IssueBase]:
|
|
444
|
+
try:
|
|
445
|
+
resp = await self.token_post(url=f"issue/by_config_id/{config_id}")
|
|
446
|
+
resp.raise_for_status()
|
|
447
|
+
content = resp.json()
|
|
448
|
+
return [IssueBase(**item) for item in content]
|
|
449
|
+
except Exception as e:
|
|
450
|
+
loguru.logger.error(f"Error while fetching issues by config id {config_id}: {e}")
|
|
451
|
+
raise e
|
|
452
|
+
|
|
453
|
+
async def call_ep(self, url: str, params: dict = None, json: dict = None, method: str = "POST") -> Response:
|
|
454
|
+
if method == "POST":
|
|
455
|
+
return await self.token_post(url=url, params=params, json=json)
|
|
456
|
+
if json is not None:
|
|
457
|
+
raise ValueError("JSON is not supported for GET requests")
|
|
458
|
+
return await self.token_get(url=url, params=params)
|
|
459
|
+
|
|
460
|
+
async def update_config_value(self, config_id: str, new_value: dict):
|
|
461
|
+
try:
|
|
462
|
+
config = await self.get_config_by_id(config_id)
|
|
463
|
+
config.config = new_value
|
|
464
|
+
update_req = json.loads(config.model_dump_json())
|
|
465
|
+
update_req["_id"] = config_id
|
|
466
|
+
resp = await self.token_post(url=f"config/update", json=update_req)
|
|
467
|
+
resp.raise_for_status()
|
|
468
|
+
return Config.model_validate(resp.json())
|
|
469
|
+
except Exception as e:
|
|
470
|
+
loguru.logger.error(f"Error while updating config {config_id}: {e}")
|
|
471
|
+
raise e
|
|
472
|
+
|
|
473
|
+
@catch_exceptions
|
|
474
|
+
async def send_email(self, email: EmailData, timeout: float = 10.0) -> Response:
|
|
475
|
+
"""Sends an email via RITA. Default timeout is 10 seconds. Large emails may take longer to send."""
|
|
476
|
+
return await self.token_post(url="integration/send_email", json=email.model_dump(), timeout=timeout)
|
|
477
|
+
|
|
478
|
+
@alru_cache(maxsize=64)
|
|
479
|
+
async def get_connection(self, connection_id: str) -> dict:
|
|
480
|
+
return await self.token_get(url=f"crossroads/network/connections/{connection_id}")
|
|
481
|
+
|
|
482
|
+
async def get_crossroads_mapping(self, req: MappingRequest) -> MappingRequest:
|
|
483
|
+
try:
|
|
484
|
+
resp = await self.token_post(url=f"crossroads_2/map", json=req.model_dump(mode="json"))
|
|
485
|
+
if resp.status_code == 499:
|
|
486
|
+
# There was an error while mapping. Parse the detail and re-raise the error.
|
|
487
|
+
js = resp.json()["detail"]
|
|
488
|
+
raise CrossroadsMappingError(**js)
|
|
489
|
+
if resp.status_code == 500:
|
|
490
|
+
raise ValueError(resp.content)
|
|
491
|
+
content = resp.json()
|
|
492
|
+
result = MappingRequest.model_validate(content)
|
|
493
|
+
return result
|
|
494
|
+
except Exception as e:
|
|
495
|
+
loguru.logger.error(f"Unexpected error while fetching Crossroads mapping: {e}")
|
|
496
|
+
raise e
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def build_single_fileconfig_dict(self, config: Config) -> FileConfig:
|
|
501
|
+
"""
|
|
502
|
+
Converts a config to File Config.
|
|
503
|
+
"""
|
|
504
|
+
file_config = FileConfig.model_validate(config.config)
|
|
505
|
+
file_config.config_id = config.id # Add the config's ID to the fileconfig obj, in case it's needed later.
|
|
506
|
+
return file_config
|
|
507
|
+
|
|
508
|
+
def build_single_probeconfig_dict(self, config: Config) -> ProbeConfig:
|
|
509
|
+
"""
|
|
510
|
+
converts a config into a ProbeConfig.
|
|
511
|
+
"""
|
|
512
|
+
return ProbeConfig.model_validate(config.config)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def build_config_dict(self, configs: List[Dict]) -> Dict[str, Union[FileConfig, ProbeConfig, GenericConfig]]:
|
|
516
|
+
"""Builds a config lkp by name and parses each config to its respective model"""
|
|
517
|
+
output = {}
|
|
518
|
+
for c in configs:
|
|
519
|
+
config = Config.model_validate(c)
|
|
520
|
+
if config.type == ConfigType.fileconfig:
|
|
521
|
+
output[config.name] = self.build_single_fileconfig_dict(config)
|
|
522
|
+
elif config.type == ConfigType.probeconfig:
|
|
523
|
+
output[config.name] = self.build_single_probeconfig_dict(config)
|
|
524
|
+
else:
|
|
525
|
+
output[config.name] = GenericConfig(config_id= c.get("_id"), config = config.config)
|
|
526
|
+
return output
|
|
527
|
+
|
|
528
|
+
def build_probeconfig_dict(self, configs: List[Config]) -> Dict[str, ProbeConfig]:
|
|
529
|
+
"""
|
|
530
|
+
Converts a list of configs of any type into a dict mapping config name to parsed ProbeConfig. Skips entries
|
|
531
|
+
that are not ProbeConfigs, so the output dict may be smaller than the input list.
|
|
532
|
+
"""
|
|
533
|
+
probe_configs = [c for c in configs if c.type == ConfigType.probeconfig]
|
|
534
|
+
output = {}
|
|
535
|
+
for config in probe_configs:
|
|
536
|
+
parsed_probe_config = ProbeConfig.model_validate(config.config)
|
|
537
|
+
output[config.name] = parsed_probe_config
|
|
538
|
+
return output
|
|
539
|
+
|
|
540
|
+
def build_fileconfig_dict(self, configs: list[dict]) -> dict[str, FileConfig]:
|
|
541
|
+
"""
|
|
542
|
+
Converts a list of configs of any type into a dict mapping config name to parsed FileConfig. Skips entries
|
|
543
|
+
that are not FileConfigs, so the output dict may be smaller than the input list.
|
|
544
|
+
"""
|
|
545
|
+
output = {}
|
|
546
|
+
for c in configs:
|
|
547
|
+
config = Config.model_validate(c)
|
|
548
|
+
if config.type == ConfigType.fileconfig:
|
|
549
|
+
file_config = FileConfig.model_validate(config.config)
|
|
550
|
+
file_config.config_id = c.get("_id") # Add the config's ID to the fileconfig obj, in case it's needed later.
|
|
551
|
+
output[config.name] = file_config
|
|
552
|
+
return output
|