omnata-plugin-runtime 0.5.6a128__py3-none-any.whl → 0.5.7a129__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,,