omnata-plugin-runtime 0.5.6a128__py3-none-any.whl → 0.5.7a129__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.
- omnata_plugin_runtime/configuration.py +31 -1
- omnata_plugin_runtime/omnata_plugin.py +73 -1
- omnata_plugin_runtime/plugin_entrypoints.py +7 -31
- {omnata_plugin_runtime-0.5.6a128.dist-info → omnata_plugin_runtime-0.5.7a129.dist-info}/METADATA +1 -1
- omnata_plugin_runtime-0.5.7a129.dist-info/RECORD +12 -0
- omnata_plugin_runtime-0.5.6a128.dist-info/RECORD +0 -12
- {omnata_plugin_runtime-0.5.6a128.dist-info → omnata_plugin_runtime-0.5.7a129.dist-info}/LICENSE +0 -0
- {omnata_plugin_runtime-0.5.6a128.dist-info → omnata_plugin_runtime-0.5.7a129.dist-info}/WHEEL +0 -0
@@ -3,12 +3,14 @@ Omnata Plugin Runtime configuration objects.
|
|
3
3
|
Includes data container classes related to plugin configuration.
|
4
4
|
"""
|
5
5
|
from __future__ import annotations
|
6
|
+
import json
|
6
7
|
import sys
|
8
|
+
import logging
|
7
9
|
from typing import Any, List, Dict, Literal, Union, Optional
|
8
10
|
from enum import Enum
|
9
11
|
|
10
12
|
from abc import ABC
|
11
|
-
from pydantic import BaseModel, Field, PrivateAttr, SerializationInfo, field_validator, model_serializer, validator # pylint: disable=no-name-in-module
|
13
|
+
from pydantic import BaseModel, Field, PrivateAttr, SerializationInfo, TypeAdapter, field_validator, model_serializer, validator # pylint: disable=no-name-in-module
|
12
14
|
|
13
15
|
if tuple(sys.version_info[:2]) >= (3, 9):
|
14
16
|
# Python 3.9 and above
|
@@ -17,6 +19,8 @@ else:
|
|
17
19
|
# Python 3.8 and below
|
18
20
|
from typing_extensions import Annotated
|
19
21
|
|
22
|
+
logger = logging.getLogger(__name__)
|
23
|
+
|
20
24
|
|
21
25
|
class MapperType(str, Enum):
|
22
26
|
FIELD_MAPPING_SELECTOR = "field_mapping_selector"
|
@@ -860,3 +864,29 @@ StoredStreamConfiguration.model_rebuild()
|
|
860
864
|
InboundSyncStreamsConfiguration.model_rebuild()
|
861
865
|
StoredFieldMappings.model_rebuild()
|
862
866
|
OutboundSyncConfigurationParameters.model_rebuild()
|
867
|
+
|
868
|
+
|
869
|
+
def get_secrets(oauth_secret_name: Optional[str], other_secrets_name: Optional[str]
|
870
|
+
) -> Dict[str, StoredConfigurationValue]:
|
871
|
+
connection_secrets = {}
|
872
|
+
import _snowflake # pylint: disable=import-error, import-outside-toplevel # type: ignore
|
873
|
+
if oauth_secret_name is not None:
|
874
|
+
connection_secrets["access_token"] = StoredConfigurationValue(
|
875
|
+
value=_snowflake.get_oauth_access_token(oauth_secret_name)
|
876
|
+
)
|
877
|
+
if other_secrets_name is not None:
|
878
|
+
try:
|
879
|
+
secret_string_content = _snowflake.get_generic_secret_string(
|
880
|
+
other_secrets_name
|
881
|
+
)
|
882
|
+
if len(secret_string_content) > 2:
|
883
|
+
other_secrets = json.loads(secret_string_content)
|
884
|
+
connection_secrets = {
|
885
|
+
**connection_secrets,
|
886
|
+
**TypeAdapter(Dict[str, StoredConfigurationValue]).validate_python(other_secrets),
|
887
|
+
}
|
888
|
+
except Exception as exception:
|
889
|
+
logger.error(f"Error parsing secrets content for secret {other_secrets_name}: {str(exception)}")
|
890
|
+
raise ValueError(f"Error parsing secrets content: {str(exception)}") from exception
|
891
|
+
return connection_secrets
|
892
|
+
|
@@ -4,6 +4,7 @@ Omnata Plugin Runtime.
|
|
4
4
|
Includes data container classes and defines the contract for a plugin.
|
5
5
|
"""
|
6
6
|
from __future__ import annotations
|
7
|
+
from inspect import signature
|
7
8
|
import sys
|
8
9
|
from typing import Union
|
9
10
|
if tuple(sys.version_info[:2]) >= (3, 9):
|
@@ -31,7 +32,7 @@ from typing import Any, Callable, Dict, Iterable, List, Literal, Optional, Type,
|
|
31
32
|
import jinja2
|
32
33
|
import pandas
|
33
34
|
from pydantic_core import to_jsonable_python
|
34
|
-
from pydantic import Field, TypeAdapter, root_validator, BaseModel
|
35
|
+
from pydantic import Field, TypeAdapter, ValidationError, create_model, root_validator, BaseModel
|
35
36
|
from dateutil.parser import parse
|
36
37
|
from jinja2 import Environment
|
37
38
|
from snowflake.connector.pandas_tools import write_pandas
|
@@ -65,6 +66,7 @@ from .configuration import (
|
|
65
66
|
StreamConfiguration,
|
66
67
|
SubscriptableBaseModel,
|
67
68
|
SyncConfigurationParameters,
|
69
|
+
get_secrets
|
68
70
|
)
|
69
71
|
from .forms import (
|
70
72
|
ConnectionMethod,
|
@@ -2220,3 +2222,73 @@ def get_nested_value(nested_dict:Dict, keys:List[str]):
|
|
2220
2222
|
if isinstance(keys, str):
|
2221
2223
|
keys = [keys]
|
2222
2224
|
return reduce(lambda d, key: d.get(key) if isinstance(d, dict) else None, keys, nested_dict)
|
2225
|
+
|
2226
|
+
def consumer_udtf(param_docs: Dict[str, Dict[str, Any]]):
|
2227
|
+
def class_decorator(cls):
|
2228
|
+
# Get the original 'process' method from the class
|
2229
|
+
original_process = getattr(cls, 'process')
|
2230
|
+
sig = signature(original_process)
|
2231
|
+
params = sig.parameters
|
2232
|
+
|
2233
|
+
# Ensure the first argument is mandatory and positional
|
2234
|
+
if list(params.keys())[0] != 'self':
|
2235
|
+
raise ValueError("The first argument should be 'self' for class methods.")
|
2236
|
+
|
2237
|
+
if list(params.keys())[1] != 'connection_parameters':
|
2238
|
+
raise ValueError("The second argument should be 'connection_parameters'.")
|
2239
|
+
|
2240
|
+
# Create a Pydantic model based on the function signature and the provided documentation
|
2241
|
+
fields = {
|
2242
|
+
name: (param.annotation, param_docs.get(name, {}).get("default", param.default))
|
2243
|
+
for name, param in params.items()
|
2244
|
+
if name != 'self' and name != 'mandatory_arg'
|
2245
|
+
}
|
2246
|
+
|
2247
|
+
DynamicModel = create_model('DynamicModel', **fields)
|
2248
|
+
|
2249
|
+
# Attach the documentation to the function
|
2250
|
+
original_process.__doc__ = original_process.__doc__ or ""
|
2251
|
+
original_process.__doc__ += "\n\nArgs:\n"
|
2252
|
+
for name, param in params.items():
|
2253
|
+
if name != 'self':
|
2254
|
+
doc = param_docs.get(name, {})
|
2255
|
+
original_process.__doc__ += f" {name} ({param.annotation.__name__}): {doc.get('description', 'No description provided.')}\n"
|
2256
|
+
|
2257
|
+
@wraps(original_process)
|
2258
|
+
def wrapped_process(self, connection_parameters, *args, **kwargs):
|
2259
|
+
if connection_parameters is None:
|
2260
|
+
raise ValueError("Connection not found")
|
2261
|
+
|
2262
|
+
if not isinstance(connection_parameters, Dict):
|
2263
|
+
raise ValueError("The first argument must be an object, the result of calling PLUGIN_CONNECTION.")
|
2264
|
+
|
2265
|
+
try:
|
2266
|
+
# Validate the arguments using the dynamic Pydantic model
|
2267
|
+
validated_args = DynamicModel(**kwargs)
|
2268
|
+
except ValidationError as e:
|
2269
|
+
raise ValueError(f"Argument validation error: {e}")
|
2270
|
+
|
2271
|
+
# convert the connection parameters dictionary to a ConnectionConfigurationParameters object which includes the real secrets
|
2272
|
+
if 'other_secrets_name' in connection_parameters:
|
2273
|
+
# this is the new way, where the sync engine only passes the name of the secret
|
2274
|
+
oauth_secrets_name = None
|
2275
|
+
if 'oauth_secret_name' in connection_parameters:
|
2276
|
+
oauth_secrets_name = connection_parameters['oauth_secret_name']
|
2277
|
+
del connection_parameters['oauth_secret_name']
|
2278
|
+
result = get_secrets(oauth_secrets_name,connection_parameters['other_secrets_name'])
|
2279
|
+
connection_parameters['connection_secrets'] = result
|
2280
|
+
del connection_parameters['other_secrets_name']
|
2281
|
+
parameters = ConnectionConfigurationParameters.model_validate(connection_parameters)
|
2282
|
+
else:
|
2283
|
+
# deprecated way, where the sync engine passes the secrets directly
|
2284
|
+
parameters = ConnectionConfigurationParameters.model_validate(connection_parameters)
|
2285
|
+
|
2286
|
+
# Pass the validated arguments to the function
|
2287
|
+
return original_process(self, parameters, *args, **validated_args.dict())
|
2288
|
+
# Replace the original 'process' method with the wrapped version
|
2289
|
+
setattr(cls, 'process', wrapped_process)
|
2290
|
+
|
2291
|
+
return cls
|
2292
|
+
|
2293
|
+
return class_decorator
|
2294
|
+
|
@@ -20,6 +20,7 @@ from .configuration import (
|
|
20
20
|
OutboundSyncStrategy,
|
21
21
|
StoredConfigurationValue,
|
22
22
|
StoredMappingValue,
|
23
|
+
get_secrets
|
23
24
|
)
|
24
25
|
from .forms import ConnectionMethod, FormInputField, FormOption
|
25
26
|
from .logging import OmnataPluginLogHandler
|
@@ -71,7 +72,7 @@ class PluginEntrypoint:
|
|
71
72
|
def sync(self, sync_request: Dict):
|
72
73
|
logger.info("Entered sync method")
|
73
74
|
request = TypeAdapter(SyncRequestPayload).validate_python(sync_request)
|
74
|
-
connection_secrets =
|
75
|
+
connection_secrets = get_secrets(
|
75
76
|
request.oauth_secret_name, request.other_secrets_name
|
76
77
|
)
|
77
78
|
omnata_log_handler = OmnataPluginLogHandler(
|
@@ -270,7 +271,7 @@ class PluginEntrypoint:
|
|
270
271
|
sync_strategy = normalise_nulls(sync_strategy)
|
271
272
|
oauth_secret_name = normalise_nulls(oauth_secret_name)
|
272
273
|
other_secrets_name = normalise_nulls(other_secrets_name)
|
273
|
-
connection_secrets =
|
274
|
+
connection_secrets = get_secrets(oauth_secret_name, other_secrets_name)
|
274
275
|
connection_parameters = TypeAdapter(
|
275
276
|
Dict[str, StoredConfigurationValue]).validate_python(connection_parameters)
|
276
277
|
sync_parameters = TypeAdapter(
|
@@ -324,7 +325,7 @@ class PluginEntrypoint:
|
|
324
325
|
logger.info("Entered list_streams method")
|
325
326
|
oauth_secret_name = normalise_nulls(oauth_secret_name)
|
326
327
|
other_secrets_name = normalise_nulls(other_secrets_name)
|
327
|
-
connection_secrets =
|
328
|
+
connection_secrets = get_secrets(oauth_secret_name, other_secrets_name)
|
328
329
|
connection_parameters = TypeAdapter(
|
329
330
|
Dict[str, StoredConfigurationValue]).validate_python(connection_parameters)
|
330
331
|
sync_parameters = TypeAdapter(
|
@@ -411,31 +412,6 @@ class PluginEntrypoint:
|
|
411
412
|
raise e
|
412
413
|
return [e.model_dump() for e in events]
|
413
414
|
|
414
|
-
def get_secrets(
|
415
|
-
self, oauth_secret_name: Optional[str], other_secrets_name: Optional[str]
|
416
|
-
) -> Dict[str, StoredConfigurationValue]:
|
417
|
-
connection_secrets = {}
|
418
|
-
import _snowflake # pylint: disable=import-error, import-outside-toplevel # type: ignore
|
419
|
-
if oauth_secret_name is not None:
|
420
|
-
connection_secrets["access_token"] = StoredConfigurationValue(
|
421
|
-
value=_snowflake.get_oauth_access_token(oauth_secret_name)
|
422
|
-
)
|
423
|
-
if other_secrets_name is not None:
|
424
|
-
try:
|
425
|
-
secret_string_content = _snowflake.get_generic_secret_string(
|
426
|
-
other_secrets_name
|
427
|
-
)
|
428
|
-
if len(secret_string_content) > 2:
|
429
|
-
other_secrets = json.loads(secret_string_content)
|
430
|
-
connection_secrets = {
|
431
|
-
**connection_secrets,
|
432
|
-
**TypeAdapter(Dict[str, StoredConfigurationValue]).validate_python(other_secrets),
|
433
|
-
}
|
434
|
-
except Exception as exception:
|
435
|
-
logger.error(f"Error parsing secrets content for secret {other_secrets_name}: {str(exception)}")
|
436
|
-
raise ValueError(f"Error parsing secrets content: {str(exception)}") from exception
|
437
|
-
return connection_secrets
|
438
|
-
|
439
415
|
def ngrok_post_tunnel_fields(
|
440
416
|
self,
|
441
417
|
connection_method: str,
|
@@ -447,7 +423,7 @@ class PluginEntrypoint:
|
|
447
423
|
logger.info("Entered ngrok_post_tunnel_fields method")
|
448
424
|
oauth_secret_name = normalise_nulls(oauth_secret_name)
|
449
425
|
other_secrets_name = normalise_nulls(other_secrets_name)
|
450
|
-
connection_secrets =
|
426
|
+
connection_secrets = get_secrets(oauth_secret_name, other_secrets_name)
|
451
427
|
connection_parameters = TypeAdapter(
|
452
428
|
Dict[str, StoredConfigurationValue]).validate_python(connection_parameters)
|
453
429
|
parameters = ConnectionConfigurationParameters(
|
@@ -493,7 +469,7 @@ class PluginEntrypoint:
|
|
493
469
|
):
|
494
470
|
logger.info("Entered connect method")
|
495
471
|
logger.info(f"Connection parameters: {connection_parameters}")
|
496
|
-
connection_secrets =
|
472
|
+
connection_secrets = get_secrets(oauth_secret_name, other_secrets_name)
|
497
473
|
|
498
474
|
from omnata_plugin_runtime.omnata_plugin import (
|
499
475
|
ConnectionConfigurationParameters,
|
@@ -540,7 +516,7 @@ class PluginEntrypoint:
|
|
540
516
|
oauth_secret_name: Optional[str],
|
541
517
|
other_secrets_name: Optional[str]):
|
542
518
|
logger.info("Entered api_limits method")
|
543
|
-
connection_secrets =
|
519
|
+
connection_secrets = get_secrets(oauth_secret_name, other_secrets_name)
|
544
520
|
from omnata_plugin_runtime.omnata_plugin import (
|
545
521
|
ConnectionConfigurationParameters,
|
546
522
|
)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
omnata_plugin_runtime/__init__.py,sha256=MS9d1whnfT_B3-ThqZ7l63QeC_8OEKTuaYV5wTwRpBA,1576
|
2
|
+
omnata_plugin_runtime/api.py,sha256=FxzTqri4no8ClkOm7vZADG8aD47jcGBCTTQDEORmOJM,6326
|
3
|
+
omnata_plugin_runtime/configuration.py,sha256=TI6GaVFhewVawBCaYN34GujY57qEP6q2nik4YpSEk5s,38100
|
4
|
+
omnata_plugin_runtime/forms.py,sha256=GzSPEwcijsoPCXEO1mHiE8ylvX_KSE5TkhwqkymA2Ss,19755
|
5
|
+
omnata_plugin_runtime/logging.py,sha256=bn7eKoNWvtuyTk7RTwBS9UARMtqkiICtgMtzq3KA2V0,3272
|
6
|
+
omnata_plugin_runtime/omnata_plugin.py,sha256=I_JTaP_mkJlVRBaegppZXo9INZ5P4Eys8EBzIGTPGnQ,115135
|
7
|
+
omnata_plugin_runtime/plugin_entrypoints.py,sha256=PFSLsYEVnWHVvSoOYTtTK2JY6pp6_8_eYP53WqLRiPE,27975
|
8
|
+
omnata_plugin_runtime/rate_limiting.py,sha256=DVQ_bc-mVLBkrU1PTns1MWXhHiLpSB5HkWCcdePtJ2A,25611
|
9
|
+
omnata_plugin_runtime-0.5.7a129.dist-info/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
|
10
|
+
omnata_plugin_runtime-0.5.7a129.dist-info/METADATA,sha256=PdXaQ9OWuTFhFRFXWqhXlfCQDYVAlP1sYkbZG6qyKkM,1985
|
11
|
+
omnata_plugin_runtime-0.5.7a129.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
12
|
+
omnata_plugin_runtime-0.5.7a129.dist-info/RECORD,,
|
@@ -1,12 +0,0 @@
|
|
1
|
-
omnata_plugin_runtime/__init__.py,sha256=MS9d1whnfT_B3-ThqZ7l63QeC_8OEKTuaYV5wTwRpBA,1576
|
2
|
-
omnata_plugin_runtime/api.py,sha256=FxzTqri4no8ClkOm7vZADG8aD47jcGBCTTQDEORmOJM,6326
|
3
|
-
omnata_plugin_runtime/configuration.py,sha256=a26Lj1-nkbAsu2HhIFDDC3VWCuhwHRG6-_a7-UrsHdY,36815
|
4
|
-
omnata_plugin_runtime/forms.py,sha256=GzSPEwcijsoPCXEO1mHiE8ylvX_KSE5TkhwqkymA2Ss,19755
|
5
|
-
omnata_plugin_runtime/logging.py,sha256=bn7eKoNWvtuyTk7RTwBS9UARMtqkiICtgMtzq3KA2V0,3272
|
6
|
-
omnata_plugin_runtime/omnata_plugin.py,sha256=XyiWPEFKoH0H8bYuTVgTacpL5OM2K3lVRjlayENuR9A,111528
|
7
|
-
omnata_plugin_runtime/plugin_entrypoints.py,sha256=JirYUbPBaN0UMh9t_uAHDdhaQZ7NUhdMJ11eHRKOoNY,29302
|
8
|
-
omnata_plugin_runtime/rate_limiting.py,sha256=DVQ_bc-mVLBkrU1PTns1MWXhHiLpSB5HkWCcdePtJ2A,25611
|
9
|
-
omnata_plugin_runtime-0.5.6a128.dist-info/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
|
10
|
-
omnata_plugin_runtime-0.5.6a128.dist-info/METADATA,sha256=hb7JVqmPYdC53jyams8wMfVFsUbCUCywgl8DFDxBgdw,1985
|
11
|
-
omnata_plugin_runtime-0.5.6a128.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
12
|
-
omnata_plugin_runtime-0.5.6a128.dist-info/RECORD,,
|
{omnata_plugin_runtime-0.5.6a128.dist-info → omnata_plugin_runtime-0.5.7a129.dist-info}/LICENSE
RENAMED
File without changes
|
{omnata_plugin_runtime-0.5.6a128.dist-info → omnata_plugin_runtime-0.5.7a129.dist-info}/WHEEL
RENAMED
File without changes
|