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.
@@ -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 = self.get_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 = self.get_secrets(oauth_secret_name, other_secrets_name)
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 = self.get_secrets(oauth_secret_name, other_secrets_name)
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 = self.get_secrets(oauth_secret_name, other_secrets_name)
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 = self.get_secrets(oauth_secret_name, other_secrets_name)
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 = self.get_secrets(oauth_secret_name, other_secrets_name)
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
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omnata-plugin-runtime
3
- Version: 0.5.6a128
3
+ Version: 0.5.7a129
4
4
  Summary: Classes and common runtime components for building and running Omnata Plugins
5
5
  Author: James Weakley
6
6
  Author-email: james.weakley@omnata.com
@@ -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,,