omnata-plugin-runtime 0.1.73__py3-none-any.whl → 0.1.80__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.
@@ -5,14 +5,24 @@ Not used in plugins.
5
5
  import sys
6
6
  import json
7
7
 
8
- from typing import Dict, List, Literal, Optional, Union, cast # pylint: disable=ungrouped-imports
8
+ from typing import (
9
+ Dict,
10
+ List,
11
+ Literal,
12
+ Optional,
13
+ Union,
14
+ cast,
15
+ ) # pylint: disable=ungrouped-imports
9
16
 
10
17
  from pydantic import BaseModel, Field # pylint: disable=no-name-in-module
11
18
  from snowflake.snowpark import Row
12
19
 
13
- from .configuration import (InboundSyncStreamsConfiguration,
14
- OutboundSyncStrategy, StoredConfigurationValue,
15
- StoredMappingValue)
20
+ from .configuration import (
21
+ InboundSyncStreamsConfiguration,
22
+ OutboundSyncStrategy,
23
+ StoredConfigurationValue,
24
+ StoredMappingValue,
25
+ )
16
26
  from .rate_limiting import ApiLimits, RateLimitState
17
27
 
18
28
  if tuple(sys.version_info[:2]) >= (3, 9):
@@ -75,7 +85,7 @@ PluginMessage = Annotated[
75
85
  PluginMessageStreamState,
76
86
  PluginMessageStreamProgressUpdate,
77
87
  PluginMessageCancelledStreams,
78
- PluginMessageAbandonedStreams
88
+ PluginMessageAbandonedStreams,
79
89
  ],
80
90
  Field(discriminator="message_type"),
81
91
  ]
@@ -105,7 +115,7 @@ class OutboundSyncRequestPayload(BaseModel):
105
115
  sync_parameters: Dict[str, StoredConfigurationValue]
106
116
  api_limit_overrides: List[ApiLimits]
107
117
  rate_limits_state: Dict[str, RateLimitState]
108
- field_mappings: StoredMappingValue
118
+ field_mappings: Optional[StoredMappingValue]
109
119
 
110
120
 
111
121
  class InboundSyncRequestPayload(BaseModel):
@@ -138,6 +148,7 @@ SyncRequestPayload = Annotated[
138
148
  Field(discriminator="sync_direction"),
139
149
  ]
140
150
 
151
+
141
152
  def handle_proc_result(query_result: List[Row]) -> Dict:
142
153
  """
143
154
  Our standard proc response is a single row with a single column, which is a JSON string
@@ -145,9 +156,11 @@ def handle_proc_result(query_result: List[Row]) -> Dict:
145
156
  Otherwise we return the data
146
157
  """
147
158
  if len(query_result) != 1:
148
- raise ValueError(f"Expected a single row result from procedure (got {len(query_result)})")
159
+ raise ValueError(
160
+ f"Expected a single row result from procedure (got {len(query_result)})"
161
+ )
149
162
  first_row = cast(Row, query_result[0])
150
163
  result = json.loads(str(first_row[0]))
151
164
  if result["success"] is not True:
152
165
  raise ValueError(result["error"])
153
- return result["data"] if "data" in result else result
166
+ return result["data"] if "data" in result else result
@@ -1,5 +1,3 @@
1
- # it's not the 1980s anymore
2
- # pylint: disable=line-too-long,multiple-imports,logging-fstring-interpolation
3
1
  """
4
2
  Omnata Plugin Runtime configuration objects.
5
3
  Includes data container classes related to plugin configuration.
@@ -14,7 +12,7 @@ from pydantic import BaseModel, Field # pylint: disable=no-name-in-module
14
12
 
15
13
  if tuple(sys.version_info[:2]) >= (3, 9):
16
14
  # Python 3.9 and above
17
- from typing import Annotated # pylint: disable=ungrouped-imports
15
+ from typing import Annotated # pylint: disable=ungrouped-imports
18
16
  else:
19
17
  # Python 3.8 and below
20
18
  from typing_extensions import Annotated
@@ -207,10 +205,10 @@ class OutboundSyncStrategy(SubscriptableBaseModel, ABC):
207
205
  name: str
208
206
  description: str
209
207
  icon_source: str = ICON_URL_CODE
210
- action_on_record_create: OutboundSyncAction = None
211
- action_on_record_update: OutboundSyncAction = None
212
- action_on_record_delete: OutboundSyncAction = None
213
- action_on_record_unchanged: OutboundSyncAction = None
208
+ action_on_record_create: Optional[OutboundSyncAction] = None
209
+ action_on_record_update: Optional[OutboundSyncAction] = None
210
+ action_on_record_delete: Optional[OutboundSyncAction] = None
211
+ action_on_record_unchanged: Optional[OutboundSyncAction] = None
214
212
  custom_strategy: bool = True
215
213
 
216
214
  def __eq__(self, other):
@@ -498,7 +496,7 @@ class ConnectionConfigurationParameters(SubscriptableBaseModel):
498
496
  raise ValueError("Connection parameters were not provided")
499
497
  if parameter_name not in self.connection_parameters.keys():
500
498
  raise ValueError(f"Connection parameter '{parameter_name}' not available")
501
- return self.connection_parameters[ # pylint: disable=unsubscriptable-object
499
+ return self.connection_parameters[ # pylint: disable=unsubscriptable-object
502
500
  parameter_name
503
501
  ]
504
502
 
@@ -516,7 +514,7 @@ class ConnectionConfigurationParameters(SubscriptableBaseModel):
516
514
  raise ValueError("Connection secrets were not provided")
517
515
  if parameter_name not in self.connection_secrets.keys():
518
516
  raise ValueError(f"Connection secret '{parameter_name}' not available")
519
- return self.connection_secrets[ # pylint: disable=unsubscriptable-object
517
+ return self.connection_secrets[ # pylint: disable=unsubscriptable-object
520
518
  parameter_name
521
519
  ]
522
520
 
@@ -559,7 +557,7 @@ class SyncConfigurationParameters(ConnectionConfigurationParameters):
559
557
  if default_value is not None:
560
558
  return {"value": default_value, "metadata": {}}
561
559
  raise ValueError(f"Sync parameter '{parameter_name}' not available")
562
- return self.sync_parameters[ # pylint: disable=unsubscriptable-object
560
+ return self.sync_parameters[ # pylint: disable=unsubscriptable-object
563
561
  parameter_name
564
562
  ]
565
563
 
@@ -580,7 +578,7 @@ class SyncConfigurationParameters(ConnectionConfigurationParameters):
580
578
  if default_value is not None:
581
579
  return StoredConfigurationValue(value=default_value)
582
580
  raise ValueError(f"Form parameter '{parameter_name}' not available")
583
- return self.current_form_parameters[ # pylint: disable=unsubscriptable-object
581
+ return self.current_form_parameters[ # pylint: disable=unsubscriptable-object
584
582
  parameter_name
585
583
  ]
586
584
 
@@ -591,7 +589,9 @@ class SyncConfigurationParameters(ConnectionConfigurationParameters):
591
589
  return_dict = {}
592
590
  for dict_key in self.current_form_parameters.keys():
593
591
  if dict_key not in exclude_fields:
594
- return_dict[dict_key] = self.current_form_parameters[ # pylint: disable=unsubscriptable-object
592
+ return_dict[
593
+ dict_key
594
+ ] = self.current_form_parameters[ # pylint: disable=unsubscriptable-object
595
595
  dict_key
596
596
  ].value
597
597
  return return_dict
@@ -1,5 +1,3 @@
1
- # it's not the 1980s anymore
2
- # pylint: disable=line-too-long,multiple-imports,logging-fstring-interpolation,too-many-arguments,no-self-argument
3
1
  """
4
2
  Contains form elements for Omnata plugin configuration
5
3
  """
@@ -207,7 +205,7 @@ class FormJinjaTemplate(SubscriptableBaseModel):
207
205
  # Everything above here has no dependencies on other BaseModels in this module
208
206
  # ----------------------------------------------------------------------------
209
207
 
210
- NewOptionCreator = ForwardRef("NewOptionCreator") # type: ignore
208
+ NewOptionCreator = ForwardRef("NewOptionCreator") # type: ignore
211
209
 
212
210
 
213
211
  class StaticFormOptionsDataSource(SubscriptableBaseModel):
@@ -390,20 +388,34 @@ class InboundSyncConfigurationForm(ConfigurationFormBase):
390
388
  fields: List[FormFieldBase] = Field(default_factory=list)
391
389
  stream_lister: StreamLister
392
390
 
391
+ class SecurityIntegrationTemplate(BaseModel):
392
+ """
393
+ Provides values used to populate a security integration instructions template, which
394
+ in turn allows the customer to create an OAuth based secret object
395
+ """
396
+ oauth_docs_url: Optional[str] = None
397
+ oauth_type: Literal["code_grant"] = "code_grant"
398
+ oauth_client_id: str = '<client id>'
399
+ oauth_client_secret: str = '<client secret>'
400
+ oauth_token_endpoint: str = '<token endpoint>'
401
+ oauth_authorization_endpoint: str = '<authorization endpoint>'
402
+ oauth_allowed_scopes: List[str] = []
403
+
393
404
 
394
405
  class ConnectionMethod(SubscriptableBaseModel):
395
406
  """
396
407
  Defines a method of connecting to an application.
397
408
  :param str data_source: The name of the connection method, e.g. "OAuth", "API Key", "Credentials"
398
409
  :param List[FormFieldBase] fields: A list of fields that are used to collect the connection information from the user.
399
- :param bool oauth: If True, the resulting form will indicate to the user that OAuth will be used.
400
- In this scenario, the oauth_parameters function will be called before the connect function.
410
+ :param Optional[SecurityIntegrationTemplate] oauth_template: If provided, the user will be guided through the process
411
+ of creating a security integration, followed by a secret and performing the OAuth flow. Once this secret is completed,
412
+ the rest of the values from the form will be captured and then the connection will be tested.
401
413
  :param str description: A markdown description of the connection method, which will be displayed to the user.
402
- This should be concise as it will be displayed in a sidebar, but you can include a link to the connection section
414
+ This should be concise as it will be displayed in a sidebar, but you can include a link to the connection section
403
415
  of the plugin's documentation.
404
416
  """
405
417
 
406
418
  name: str
407
419
  fields: List[FormFieldBase]
408
- oauth: bool = False
420
+ oauth_template: Optional[SecurityIntegrationTemplate] = None
409
421
  description: str = ""
@@ -37,7 +37,7 @@ class CustomLogger(logging.getLoggerClass()):
37
37
  if isinstance(exc_value, ValidationError):
38
38
  record.msg = exc_value.errors()
39
39
  record.exc_info = (exc_type, None, tb)
40
- super().handleError(record) # type: ignore
40
+ super().handleError(record) # type: ignore
41
41
 
42
42
 
43
43
  class OmnataPluginLogHandler(logging.Handler):
@@ -66,11 +66,13 @@ class OmnataPluginLogHandler(logging.Handler):
66
66
 
67
67
  def emit(self, record: logging.LogRecord):
68
68
  if hasattr(record, "stream_name"):
69
- stream_name: str = record.stream_name # type: ignore
69
+ stream_name: str = record.stream_name # type: ignore
70
70
  if record.levelno >= logging.ERROR:
71
71
  self.stream_has_errors[stream_name] = True
72
72
 
73
- def register(self, logging_level: str, additional_loggers: Optional[List[str]] = None):
73
+ def register(
74
+ self, logging_level: str, additional_loggers: Optional[List[str]] = None
75
+ ):
74
76
  """
75
77
  Register the handler with the omnata_plugin namespace
76
78
  """
@@ -1,5 +1,4 @@
1
- # it's not the 1980s anymore
2
- # pylint: disable=line-too-long,multiple-imports,logging-fstring-interpolation
1
+ # pylint: disable=protected-access
3
2
  """
4
3
  Omnata Plugin Runtime.
5
4
  Includes data container classes and defines the contract for a plugin.
@@ -28,23 +27,40 @@ from snowflake.connector.pandas_tools import write_pandas
28
27
  from snowflake.snowpark import Session
29
28
  from snowflake.snowpark.functions import col
30
29
 
31
- from .api import (PluginMessage, PluginMessageAbandonedStreams,
32
- PluginMessageCancelledStreams, PluginMessageCurrentActivity,
33
- PluginMessageStreamProgressUpdate, PluginMessageStreamState,
34
- handle_proc_result)
35
- from .configuration import (STANDARD_OUTBOUND_SYNC_ACTIONS,
36
- ConnectionConfigurationParameters,
37
- InboundSyncConfigurationParameters,
38
- OutboundSyncAction,
39
- OutboundSyncConfigurationParameters,
40
- OutboundSyncStrategy, StoredConfigurationValue,
41
- StoredMappingValue, StoredStreamConfiguration,
42
- StreamConfiguration, SubscriptableBaseModel,
43
- SyncConfigurationParameters)
44
- from .forms import (ConnectionMethod, InboundSyncConfigurationForm,
45
- OutboundSyncConfigurationForm)
46
- from .rate_limiting import (ApiLimits, HttpMethodType,
47
- InterruptedWhileWaitingException, RateLimitState)
30
+ from .api import (
31
+ PluginMessage,
32
+ PluginMessageAbandonedStreams,
33
+ PluginMessageCancelledStreams,
34
+ PluginMessageCurrentActivity,
35
+ PluginMessageStreamProgressUpdate,
36
+ PluginMessageStreamState,
37
+ handle_proc_result,
38
+ )
39
+ from .configuration import (
40
+ STANDARD_OUTBOUND_SYNC_ACTIONS,
41
+ ConnectionConfigurationParameters,
42
+ InboundSyncConfigurationParameters,
43
+ OutboundSyncAction,
44
+ OutboundSyncConfigurationParameters,
45
+ OutboundSyncStrategy,
46
+ StoredConfigurationValue,
47
+ StoredMappingValue,
48
+ StoredStreamConfiguration,
49
+ StreamConfiguration,
50
+ SubscriptableBaseModel,
51
+ SyncConfigurationParameters,
52
+ )
53
+ from .forms import (
54
+ ConnectionMethod,
55
+ InboundSyncConfigurationForm,
56
+ OutboundSyncConfigurationForm,
57
+ )
58
+ from .rate_limiting import (
59
+ ApiLimits,
60
+ HttpMethodType,
61
+ InterruptedWhileWaitingException,
62
+ RateLimitState,
63
+ )
48
64
 
49
65
  logger = getLogger(__name__)
50
66
  SortDirectionType = Literal["asc", "desc"]
@@ -142,7 +158,7 @@ class SyncRequest(ABC):
142
158
  self._run_id = run_id
143
159
  self.api_limits = api_limits
144
160
  self.rate_limit_state = rate_limit_state
145
- self._apply_results = None # this will be re-initialised by subclasses
161
+ self._apply_results = None # this will be re-initialised by subclasses
146
162
  # these deal with applying the results, not sure they belong here
147
163
  self._apply_results_lock = threading.Lock()
148
164
  # Snowflake connector appears to not be thread safe
@@ -204,7 +220,7 @@ class SyncRequest(ABC):
204
220
  Designed to be run in a thread, this method checks to see if the sync run has been cancelled.
205
221
  """
206
222
  while not cancellation_token.is_set():
207
- logger.info("cancel checking worked checking for results")
223
+ logger.info("cancel checking worker checking for results")
208
224
 
209
225
  with self._snowflake_query_lock:
210
226
  try:
@@ -344,7 +360,7 @@ class HttpRateLimiting:
344
360
  It does this by patching http.client.HTTPConnection.putrequest
345
361
  """
346
362
  self_outer = self
347
- self.original_putrequest = http.client.HTTPConnection.putrequest # type: ignore
363
+ self.original_putrequest = http.client.HTTPConnection.putrequest # type: ignore
348
364
 
349
365
  def new_putrequest(
350
366
  self,
@@ -372,10 +388,10 @@ class HttpRateLimiting:
372
388
  self, method, url, skip_host, skip_accept_encoding
373
389
  )
374
390
 
375
- http.client.HTTPConnection.putrequest = new_putrequest # type: ignore
391
+ http.client.HTTPConnection.putrequest = new_putrequest # type: ignore
376
392
 
377
393
  def __exit__(self, exc_type, exc_value, traceback):
378
- http.client.HTTPConnection.putrequest = self.original_putrequest # type: ignore
394
+ http.client.HTTPConnection.putrequest = self.original_putrequest # type: ignore
379
395
 
380
396
 
381
397
  class OutboundSyncRequest(SyncRequest):
@@ -554,23 +570,29 @@ class OutboundSyncRequest(SyncRequest):
554
570
  # use a random table name with a random string to avoid collisions
555
571
  with self._snowflake_query_lock:
556
572
  success, nchunks, nrows, _ = write_pandas(
557
- conn=self._session._conn._cursor.connection, # pylint: disable=protected-access
573
+ conn=self._session._conn._cursor.connection, # pylint: disable=protected-access
558
574
  df=self._preprocess_results_dataframe(results_df),
559
575
  quote_identifiers=False,
560
576
  table_name=self._full_results_table_name,
561
577
  auto_create_table=False,
562
578
  )
563
579
  if not success:
564
- raise ValueError(f"Failed to write results to table {self._full_results_table_name}")
565
- logger.info(f"Wrote {nrows} rows and {nchunks} chunks to table {self._full_results_table_name}")
580
+ raise ValueError(
581
+ f"Failed to write results to table {self._full_results_table_name}"
582
+ )
583
+ logger.info(
584
+ f"Wrote {nrows} rows and {nchunks} chunks to table {self._full_results_table_name}"
585
+ )
566
586
 
567
- def __dataframe_wrapper(self, data_frame:pandas.DataFrame, render_jinja: bool = True) -> pandas.DataFrame:
587
+ def __dataframe_wrapper(
588
+ self, data_frame: pandas.DataFrame, render_jinja: bool = True
589
+ ) -> pandas.DataFrame:
568
590
  """
569
591
  Takes care of some common stuff we need to do for each dataframe for outbound syncs.
570
592
  Parses the JSON in the transformed record column (Snowflake passes it as a string).
571
593
  Also when the mapper is a jinja template, renders it.
572
594
  """
573
- #if data_frame is None:
595
+ # if data_frame is None:
574
596
  # logger.info(
575
597
  # "Dataframe wrapper skipping pre-processing as dataframe is None"
576
598
  # )
@@ -672,9 +694,7 @@ class OutboundSyncRequest(SyncRequest):
672
694
  with self._snowflake_query_lock:
673
695
  dataframe = (
674
696
  self._session.table(self._full_records_table_name)
675
- .filter(
676
- (col("SYNC_ACTION").in_(sync_action_names)) # type: ignore
677
- )
697
+ .filter((col("SYNC_ACTION").in_(sync_action_names))) # type: ignore
678
698
  .select(
679
699
  col("IDENTIFIER"), col("SYNC_ACTION"), col("TRANSFORMED_RECORD")
680
700
  )
@@ -828,9 +848,7 @@ class InboundSyncRequest(SyncRequest):
828
848
  if stream.stream_name not in self._completed_streams
829
849
  ]
830
850
  self._plugin_message(
831
- message=PluginMessageAbandonedStreams(
832
- abandoned_streams = abandoned_streams
833
- )
851
+ message=PluginMessageAbandonedStreams(abandoned_streams=abandoned_streams)
834
852
  )
835
853
 
836
854
  def enqueue_results(self, stream_name: str, results: List[Dict], new_state: Any):
@@ -932,7 +950,7 @@ class InboundSyncRequest(SyncRequest):
932
950
  [
933
951
  {
934
952
  "RECORD_DATA": self._convert_by_json_schema(
935
- stream_name, data, stream_obj.json_schema # type: ignore
953
+ stream_name, data, stream_obj.json_schema # type: ignore
936
954
  )
937
955
  }
938
956
  for data in results
@@ -987,7 +1005,7 @@ class InboundSyncRequest(SyncRequest):
987
1005
  f"Applying {len(results_df)} results to {self._full_results_table_name}"
988
1006
  )
989
1007
  success, nchunks, nrows, _ = write_pandas(
990
- conn=self._session._conn._cursor.connection, # pylint: disable=protected-access
1008
+ conn=self._session._conn._cursor.connection, # pylint: disable=protected-access
991
1009
  df=results_df,
992
1010
  table_name=self._full_results_table_name,
993
1011
  quote_identifiers=False, # already done in get_temp_table_name
@@ -995,8 +1013,12 @@ class InboundSyncRequest(SyncRequest):
995
1013
  table_type="transient",
996
1014
  )
997
1015
  if not success:
998
- raise ValueError(f"Failed to write results to table {self._full_results_table_name}")
999
- logger.info(f"Wrote {nrows} rows and {nchunks} chunks to table {self._full_results_table_name}")
1016
+ raise ValueError(
1017
+ f"Failed to write results to table {self._full_results_table_name}"
1018
+ )
1019
+ logger.info(
1020
+ f"Wrote {nrows} rows and {nchunks} chunks to table {self._full_results_table_name}"
1021
+ )
1000
1022
  # temp tables aren't allowed
1001
1023
  # snowflake_df = self._session.create_dataframe(results_df)
1002
1024
  # snowflake_df.write.save_as_table(table_name=temp_table,
@@ -1016,23 +1038,6 @@ class InboundSyncRequest(SyncRequest):
1016
1038
  self._plugin_message(PluginMessageStreamState(stream_state=self._latest_states))
1017
1039
 
1018
1040
 
1019
- class OAuthParameters(SubscriptableBaseModel):
1020
- """
1021
- Encapsulates a set of OAuth Parameters
1022
- """
1023
-
1024
- scope: str
1025
- authorization_url: str
1026
- access_token_url: str
1027
- client_id: str
1028
- state: Optional[str] = None
1029
- response_type: str = "code"
1030
- access_type: str = "offline"
1031
-
1032
- class Config:
1033
- extra = "allow" # OAuth can contain extra fields
1034
-
1035
-
1036
1041
  class ConnectResponse(SubscriptableBaseModel):
1037
1042
  """
1038
1043
  Encapsulates the response to a connection request. This is used to pass back any additional
@@ -1063,14 +1068,11 @@ class BillingEvent(BaseModel):
1063
1068
  class BillingEventRequest(BaseModel):
1064
1069
  """
1065
1070
  Represents a request to provide billing events for that day.
1066
- The Omnata engine provides the number of runs for the most frequent inbound and outbound syncs, and the sync ids
1067
1071
  """
1068
1072
 
1069
1073
  billing_schedule: Literal["DAILY"]
1070
- inbound_most_frequent_run_count: int
1071
- inbound_most_frequent_sync_id: int
1072
- outbound_most_frequent_run_count: int
1073
- outbound_most_frequent_sync_id: int
1074
+ has_active_inbound: bool
1075
+ has_active_outbound: bool
1074
1076
 
1075
1077
 
1076
1078
  # BillingEventRequest = Annotated[Union[DailyBillingEventRequest,...],Field(discriminator='billing_schedule')]
@@ -1171,21 +1173,6 @@ class OmnataPlugin(ABC):
1171
1173
  """
1172
1174
  raise NotImplementedError("Your plugin class must implement the connect method")
1173
1175
 
1174
- def oauth_parameters(
1175
- self, parameters: ConnectionConfigurationParameters
1176
- ) -> OAuthParameters:
1177
- """
1178
- This function is called for any connection method where the "oauth" flag is set to true.
1179
- Connection Parameters are provided in case they are needed to construct the OAuth parameters.
1180
-
1181
- :param PluginConfigurationParameters parameters the parameters of the sync, as configured by the user
1182
- :return A OAuthParameters, which contains information to commence an OAuth flow
1183
- :rtype OAuthParameters
1184
- """
1185
- raise NotImplementedError(
1186
- "Your plugin class must implement the oauth_parameters method"
1187
- )
1188
-
1189
1176
  def sync_outbound(
1190
1177
  self,
1191
1178
  parameters: OutboundSyncConfigurationParameters,
@@ -1222,7 +1209,6 @@ class OmnataPlugin(ABC):
1222
1209
  source_types: a dictionary of field names to the original SQL type of the source column/literal/expression (before conversion to variant), as returned by SYSTEM$TYPEOF.
1223
1210
  Leveraging this information may be simpler than trying to parse the transformed values to determine if the original type is compatible
1224
1211
  """
1225
- pass
1226
1212
 
1227
1213
  def sync_inbound(
1228
1214
  self,
@@ -1366,10 +1352,14 @@ def __managed_outbound_processing_worker(
1366
1352
  logger.info(
1367
1353
  f"worker {worker_index} processing. Cancelled: {cancellation_token.is_set()}"
1368
1354
  )
1369
- assert plugin_class_obj._sync_request is not None # pylint: disable=protected-access
1370
- if datetime.datetime.now() > plugin_class_obj._sync_request._run_deadline: # pylint: disable=protected-access
1355
+ assert (
1356
+ plugin_class_obj._sync_request is not None
1357
+ ) # pylint: disable=protected-access
1358
+ if (
1359
+ datetime.datetime.now() > plugin_class_obj._sync_request._run_deadline
1360
+ ): # pylint: disable=protected-access
1371
1361
  # if we've reached the deadline for the run, end it
1372
- plugin_class_obj._sync_request.apply_deadline_reached() # pylint: disable=protected-access
1362
+ plugin_class_obj._sync_request.apply_deadline_reached() # pylint: disable=protected-access
1373
1363
  return
1374
1364
  records_df = next(dataframe_generator)
1375
1365
  logger.info(f"records returned from dataframe generator: {records_df}")
@@ -1406,8 +1396,12 @@ def __managed_outbound_processing_worker(
1406
1396
 
1407
1397
  # we want to write the results of the batch back to Snowflake, so we
1408
1398
  # enqueue them and they'll be picked up by the apply_results worker
1409
- outbound_sync_request = cast(OutboundSyncRequest, plugin_class_obj._sync_request) # pylint: disable=protected-access
1410
- outbound_sync_request.enqueue_results(results_df) # pylint: disable=protected-access
1399
+ outbound_sync_request = cast(
1400
+ OutboundSyncRequest, plugin_class_obj._sync_request
1401
+ ) # pylint: disable=protected-access
1402
+ outbound_sync_request.enqueue_results(
1403
+ results_df
1404
+ ) # pylint: disable=protected-access
1411
1405
  logger.info(
1412
1406
  f"worker {worker_index} applied results, marking queue task as done"
1413
1407
  )
@@ -1430,10 +1424,10 @@ def managed_outbound_processing(concurrency: int, batch_size: int):
1430
1424
 
1431
1425
  def actual_decorator(method):
1432
1426
  @wraps(method)
1433
- def _impl(self:OmnataPlugin, *method_args, **method_kwargs):
1427
+ def _impl(self: OmnataPlugin, *method_args, **method_kwargs):
1434
1428
  logger.info(f"method_args: {method_args}")
1435
1429
  logger.info(f"method_kwargs: {method_kwargs}")
1436
- if self._sync_request is None: # pylint: disable=protected-access
1430
+ if self._sync_request is None: # pylint: disable=protected-access
1437
1431
  raise ValueError(
1438
1432
  "To use the managed_outbound_processing decorator, you must attach a sync request to the plugin instance (via the _sync_request property)"
1439
1433
  )
@@ -1,4 +1,3 @@
1
- # pylint: disable=logging-fstring-interpolation
2
1
  import datetime
3
2
  import importlib
4
3
  import json
@@ -101,9 +100,9 @@ class PluginEntrypoint:
101
100
  # if any endpoint categories have no state, give them an empty state
102
101
  for api_limit in api_limits:
103
102
  if api_limit.endpoint_category not in request.rate_limits_state:
104
- request.rate_limits_state[
105
- api_limit.endpoint_category
106
- ] = RateLimitState(wait_until=None,previous_request_timestamps=None)
103
+ request.rate_limits_state[api_limit.endpoint_category] = RateLimitState(
104
+ wait_until=None, previous_request_timestamps=None
105
+ )
107
106
  logger.info(
108
107
  f"Rate limits state: {json.dumps(request.rate_limits_state, default=pydantic.json.pydantic_encoder)}"
109
108
  )
@@ -318,11 +317,11 @@ class PluginEntrypoint:
318
317
  events: List[BillingEvent] = self._plugin_instance.create_billing_events(
319
318
  request
320
319
  )
321
- # create each billing event, waiting a minute between each one
320
+ # create each billing event, waiting a second between each one
322
321
  first_time = True
323
322
  for billing_event in events:
324
323
  if not first_time:
325
- time.sleep(60)
324
+ time.sleep(1)
326
325
  else:
327
326
  first_time = False
328
327
  current_time = int(time.time() * 1000)
@@ -342,8 +341,8 @@ class PluginEntrypoint:
342
341
  $${json.dumps(billing_event.additional_info)}$$)
343
342
  """
344
343
  logger.info(f"Executing billing event query: {event_query}")
345
- handle_proc_result(session.sql(event_query).collect())
346
-
344
+ result = session.sql(event_query).collect()
345
+ logger.info(f"Billing event result: {result}")
347
346
  return [e.dict() for e in events]
348
347
 
349
348
  def get_secrets(
@@ -362,14 +361,15 @@ class PluginEntrypoint:
362
361
  secret_string_content = _snowflake.get_generic_secret_string(
363
362
  other_secrets_name
364
363
  )
365
- other_secrets = json.loads(secret_string_content)
364
+ if len(secret_string_content) > 2:
365
+ other_secrets = json.loads(secret_string_content)
366
+ connection_secrets = {
367
+ **connection_secrets,
368
+ **parse_obj_as(Dict[str, StoredConfigurationValue], other_secrets),
369
+ }
366
370
  except Exception as exception:
367
- logger.error(f"Error parsing secrets content: {str(exception)}")
371
+ logger.error(f"Error parsing secrets content for secret {other_secrets_name}: {str(exception)}")
368
372
  raise ValueError("Error parsing secrets content:") from exception
369
- connection_secrets = {
370
- **connection_secrets,
371
- **parse_obj_as(Dict[str, StoredConfigurationValue], other_secrets),
372
- }
373
373
  return connection_secrets
374
374
 
375
375
  def network_addresses(self, method: str, connection_parameters: Dict) -> List[str]:
@@ -411,9 +411,7 @@ class PluginEntrypoint:
411
411
  connection_parameters=parse_obj_as(
412
412
  Dict[str, StoredConfigurationValue], connection_parameters
413
413
  ),
414
- connection_secrets=parse_obj_as(
415
- Dict[str, StoredConfigurationValue], connection_secrets
416
- ),
414
+ connection_secrets=connection_secrets
417
415
  )
418
416
  )
419
417
  # the connect method can also return more network addresses. If so, we need to update the
@@ -1,5 +1,3 @@
1
- # it's not the 1980s anymore
2
- # pylint: disable=line-too-long,multiple-imports,logging-fstring-interpolation
3
1
  """
4
2
  Contains functionality for limiting http requests made by Omnata plugins
5
3
  """
@@ -25,6 +23,7 @@ HttpMethodType = Literal[
25
23
  "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"
26
24
  ]
27
25
 
26
+
28
27
  class HttpRequestMatcher(SubscriptableBaseModel):
29
28
  """
30
29
  A class used to match an HTTP request
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omnata-plugin-runtime
3
- Version: 0.1.73
4
- Summary: A development kit to assist with building, testing and publishing Omnata Plugins
3
+ Version: 0.1.80
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
7
7
  Requires-Python: >=3.10,<3.11
@@ -11,7 +11,7 @@ Requires-Dist: jinja2 (>=3,<4)
11
11
  Requires-Dist: pandas
12
12
  Requires-Dist: pydantic (>=1,<2)
13
13
  Requires-Dist: requests (>=2,<3)
14
- Requires-Dist: snowflake-snowpark-python (>=1.5.1,<2.0.0)
14
+ Requires-Dist: snowflake-snowpark-python (>=1,<2)
15
15
  Description-Content-Type: text/markdown
16
16
 
17
17
  # omnata-plugin-runtime
@@ -0,0 +1,12 @@
1
+ omnata_plugin_runtime/__init__.py,sha256=w63LVME5nY-hQ4BBzfacy9kvTunwqHGs8iiSPGAX2ns,1214
2
+ omnata_plugin_runtime/api.py,sha256=vCDTCxPZ5rIhi8aSM6Z0TXWHtGpbCoNvCnM3mKa-47Q,5591
3
+ omnata_plugin_runtime/configuration.py,sha256=7P1g8ryOqiXULhKnosAp_uS-h4bZP7i0VZOQ3Izmsac,29513
4
+ omnata_plugin_runtime/forms.py,sha256=AEj74Wko89zBSgucVfPRctU-MI-AuYsIOEBDPhDi6Hc,15050
5
+ omnata_plugin_runtime/logging.py,sha256=ne1sLh5cBkjdRS54B30PGc5frABgjy0sF1_2RMcJ_Tk,3012
6
+ omnata_plugin_runtime/omnata_plugin.py,sha256=LLn4o8xZHXFpdfhIgUiC-eIbvhZOGfhNeqZVsjRQxB8,74217
7
+ omnata_plugin_runtime/plugin_entrypoints.py,sha256=RyOtV2OZ1gRmlXJKpg7EwyWaUK_5oNAGDymXgInMnck,21267
8
+ omnata_plugin_runtime/rate_limiting.py,sha256=OnFnCdMenpMpAZYumpe6mypRnMmDl1Q02vzlgmQgiw0,10733
9
+ omnata_plugin_runtime-0.1.80.dist-info/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
10
+ omnata_plugin_runtime-0.1.80.dist-info/METADATA,sha256=eIByiBLYOznPEF_zymN5f7q7UQpNRQAqSRzld1zyIsI,987
11
+ omnata_plugin_runtime-0.1.80.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
12
+ omnata_plugin_runtime-0.1.80.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.6.1
2
+ Generator: poetry-core 1.7.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,12 +0,0 @@
1
- omnata_plugin_runtime/__init__.py,sha256=w63LVME5nY-hQ4BBzfacy9kvTunwqHGs8iiSPGAX2ns,1214
2
- omnata_plugin_runtime/api.py,sha256=hxEOcT_CWb0jbQ1ahcHCBPhXAa0N8fLSg4fcADopRfQ,5563
3
- omnata_plugin_runtime/configuration.py,sha256=HjXeuJEKvYXPCXLmlSwntvRq67sOewxDmoMDQ3T0yB4,29537
4
- omnata_plugin_runtime/forms.py,sha256=8n6z7dVm4YnVHGeHMyg5ccLW6ZAWxB9UUgVZDuaXrXo,14446
5
- omnata_plugin_runtime/logging.py,sha256=QjM4x6Hpix7UN_9qVRx24HwtICCfOIS6w0cptgH-DFE,2996
6
- omnata_plugin_runtime/omnata_plugin.py,sha256=sJhTegSehM-reszYzzWQ1zQNLP1yiEYDFMEod95SSKA,75590
7
- omnata_plugin_runtime/plugin_entrypoints.py,sha256=jpohxu9vUhKzaukkHhat0xGDu5O-GZ2UfFgqm48iJco,21239
8
- omnata_plugin_runtime/rate_limiting.py,sha256=8DBz-OxzBGUCoZqWe8fJvT4TGIs4b3WFZgO--9_UbF0,10840
9
- omnata_plugin_runtime-0.1.73.dist-info/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
10
- omnata_plugin_runtime-0.1.73.dist-info/METADATA,sha256=Wk66C8wHMAED06sEhH-5txz9_ZdR8igtxt2oymfVWZk,998
11
- omnata_plugin_runtime-0.1.73.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
12
- omnata_plugin_runtime-0.1.73.dist-info/RECORD,,