omnata-plugin-runtime 0.5.0a113__tar.gz → 0.5.2a116__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omnata-plugin-runtime
3
- Version: 0.5.0a113
3
+ Version: 0.5.2a116
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
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "omnata-plugin-runtime"
3
- version = "0.5.0-a113"
3
+ version = "0.5.2-a116"
4
4
  description = "Classes and common runtime components for building and running Omnata Plugins"
5
5
  authors = ["James Weakley <james.weakley@omnata.com>"]
6
6
  readme = "README.md"
@@ -8,7 +8,7 @@ from typing import Any, List, Dict, Literal, Union, Optional
8
8
  from enum import Enum
9
9
 
10
10
  from abc import ABC
11
- from pydantic import BaseModel, Field, PrivateAttr, validator # pylint: disable=no-name-in-module
11
+ from pydantic import BaseModel, Field, PrivateAttr, SerializationInfo, model_serializer, validator # pylint: disable=no-name-in-module
12
12
 
13
13
  if tuple(sys.version_info[:2]) >= (3, 9):
14
14
  # Python 3.9 and above
@@ -110,16 +110,18 @@ class OutboundSyncAction(SubscriptableBaseModel, ABC):
110
110
  **STANDARD_OUTBOUND_SYNC_ACTIONS[data["action_name"]]().__dict__,
111
111
  }
112
112
  super().__init__(**data)
113
-
114
- def dict(self, *args, trim: bool = True, **kwargs) -> Dict[str, any]:
115
- """
116
- OutboundSyncStrategy objects can be serialized much smaller if the instance is of a Standard strategy that resides in the
117
- Omnata plugin runtime. This method trims out the unneeded attributes in these scenarios.
118
- This method also trims the actions for this strategy if it's a custom one.
119
-
120
- """
121
- excluded_fields = {} if not trim or self.custom_action else {"description"}
122
- return super().dict(exclude=excluded_fields)
113
+
114
+ @model_serializer(mode='wrap')
115
+ def ser_model(self,handler,info:SerializationInfo) -> Dict[str, Any]:
116
+ serialized:Dict[str,Any] = handler(self)
117
+ if not self.custom_action and (info.exclude_none is None or info.exclude_none == False):
118
+ return {k:v for k,v in serialized.items() if k not in [
119
+ "description"]}
120
+ return serialized
121
+
122
+ def model_dump_no_trim(self) -> Dict[str, Any]:
123
+ # we use our own special include value to signal not to trim
124
+ return self.model_dump(exclude_none=True)
123
125
 
124
126
 
125
127
  class CreateSyncAction(OutboundSyncAction):
@@ -227,7 +229,7 @@ class OutboundSyncStrategy(SubscriptableBaseModel, ABC):
227
229
  action_on_record_unchanged: Optional[OutboundSyncAction] = None
228
230
  custom_strategy: bool = True
229
231
 
230
- def __eq__(self, other):
232
+ def __eq__(self, other:OutboundSyncStrategy):
231
233
  if hasattr(other, 'custom_strategy') and hasattr(other, 'name'):
232
234
  return (
233
235
  self.custom_strategy == other.custom_strategy
@@ -251,27 +253,23 @@ class OutboundSyncStrategy(SubscriptableBaseModel, ABC):
251
253
  }
252
254
  super().__init__(**data)
253
255
 
254
- def dict(self, *args, trim: bool = True, **kwargs) -> Dict[str, any]:
255
- """
256
- OutboundSyncStrategy objects can be serialized much smaller if the instance is of a Standard strategy that resides in the
257
- Omnata plugin runtime. This method trims out the unneeded attributes in these scenarios.
258
- This method also trims the actions for this strategy if it's a custom one.
259
-
260
- """
261
- excluded_fields = (
262
- {}
263
- if not trim or self.custom_strategy
264
- else {
256
+ @model_serializer(mode='wrap')
257
+ def ser_model(self,handler,info:SerializationInfo) -> Dict[str, Any]:
258
+ serialized:Dict[str,Any] = handler(self)
259
+ if not self.custom_strategy and (info.exclude_none is None or info.exclude_none == False):
260
+ return {k:v for k,v in serialized.items() if k not in [
265
261
  "description",
266
262
  "icon_source",
267
263
  "action_on_record_create",
268
264
  "action_on_record_update",
269
265
  "action_on_record_delete",
270
- "action_on_record_unchanged",
271
- }
272
- )
273
- return super().dict(exclude=excluded_fields)
274
-
266
+ "action_on_record_unchanged"]}
267
+ return serialized
268
+
269
+ def model_dump_no_trim(self) -> Dict[str, Any]:
270
+ # we use our own special include value to signal not to trim
271
+ return self.model_dump(exclude_none=True)
272
+
275
273
 
276
274
  class CreateSyncStrategy(OutboundSyncStrategy):
277
275
  """
@@ -233,7 +233,7 @@ class PluginEntrypoint:
233
233
  stream_errors=omnata_log_handler.stream_global_errors,
234
234
  total_records_estimate=inbound_sync_request._total_records_estimate
235
235
  )
236
- return_dict["final_progress_update"] = final_progress_update.dict()
236
+ return_dict["final_progress_update"] = final_progress_update.model_dump()
237
237
  if inbound_sync_request.deadline_reached:
238
238
  # if we actually hit the deadline, this is flagged by the cancellation checking worker and the cancellation
239
239
  # token is set. We throw it here as an error since that's currently how it flows back to the engine with a DELAYED state
@@ -305,10 +305,10 @@ class PluginEntrypoint:
305
305
  )
306
306
  script_result = the_function(parameters)
307
307
  if isinstance(script_result, BaseModel):
308
- script_result = script_result.dict()
308
+ script_result = script_result.model_dump()
309
309
  elif isinstance(script_result, List):
310
310
  if len(script_result) > 0 and isinstance(script_result[0], BaseModel):
311
- script_result = [r.dict() for r in script_result]
311
+ script_result = [r.model_dump() for r in script_result]
312
312
  return script_result
313
313
 
314
314
  def inbound_list_streams(
@@ -341,10 +341,10 @@ class PluginEntrypoint:
341
341
 
342
342
  script_result = self._plugin_instance.inbound_stream_list(parameters)
343
343
  if isinstance(script_result, BaseModel):
344
- script_result = script_result.dict()
344
+ script_result = script_result.model_dump()
345
345
  elif isinstance(script_result, List):
346
346
  if len(script_result) > 0 and isinstance(script_result[0], BaseModel):
347
- script_result = [r.dict() for r in script_result]
347
+ script_result = [r.model_dump() for r in script_result]
348
348
  return script_result
349
349
 
350
350
 
@@ -365,13 +365,13 @@ class PluginEntrypoint:
365
365
  script_result = the_function(stored_value)
366
366
  if not isinstance(script_result, FormOption):
367
367
  raise ValueError(f"Expected a FormOption from function {function_name}, got {type(script_result)}")
368
- results.append(script_result.dict())
368
+ results.append(script_result.model_dump())
369
369
  return results
370
370
 
371
371
  def connection_form(self):
372
372
  logger.info("Entered connection_form method")
373
373
  form: List[ConnectionMethod] = self._plugin_instance.connection_form()
374
- return [f.dict() for f in form]
374
+ return [f.model_dump() for f in form]
375
375
 
376
376
  def create_billing_events(self, session, event_request: Dict):
377
377
  logger.info("Entered create_billing_events method")
@@ -408,7 +408,7 @@ class PluginEntrypoint:
408
408
  logger.warn('Billing event creation failed due to running internally to Omnata')
409
409
  else:
410
410
  raise e
411
- return [e.dict() for e in events]
411
+ return [e.model_dump() for e in events]
412
412
 
413
413
  def get_secrets(
414
414
  self, oauth_secret_name: Optional[str], other_secrets_name: Optional[str]
@@ -461,7 +461,7 @@ class PluginEntrypoint:
461
461
  script_result = the_function(parameters)
462
462
  if isinstance(script_result, List):
463
463
  if len(script_result) > 0 and isinstance(script_result[0], BaseModel):
464
- script_result = [r.dict() for r in script_result]
464
+ script_result = [r.model_dump() for r in script_result]
465
465
  else:
466
466
  raise ValueError(f"Expected a List from function {function_name}, got {type(script_result)}")
467
467
  return script_result
@@ -531,7 +531,7 @@ class PluginEntrypoint:
531
531
  f"alter network rule {network_rule_name} set value_list = ({rule_values_string})"
532
532
  ).collect()
533
533
 
534
- return connect_response.dict()
534
+ return connect_response.model_dump()
535
535
 
536
536
  def api_limits(self,
537
537
  method:str,
@@ -564,7 +564,7 @@ class PluginEntrypoint:
564
564
  # There's a bit of parsing here that could possibly be done outside of the handler function, but this shouldn't be too expensive
565
565
  sync_parameters: Dict[str, StoredConfigurationValue] = TypeAdapter(
566
566
  Dict[str, StoredConfigurationValue]).validate_python(sync_parameters)
567
- field_mappings: StoredMappingValue = StoredMappingValue.model_validate(field_mappings)
567
+ field_mappings: StoredMappingValue = TypeAdapter(StoredMappingValue).validate_python(field_mappings)
568
568
  return self._plugin_instance.outbound_record_validator(
569
569
  sync_parameters, field_mappings, transformed_record, source_types
570
570
  )
@@ -178,10 +178,9 @@ class RateLimitState(SubscriptableBaseModel):
178
178
  [],
179
179
  description="A list of timestamps where previous requests have been made, used to calculate the next request time",
180
180
  )
181
-
182
- _request_timestamps_lock: threading.Lock = PrivateAttr( # or use Any to annotate the type and use Field to initialize
183
- default_factory=lambda: threading.Lock()
184
- )
181
+ # can't set this as a class variable because it's not serializable
182
+ # TODO: probably shouldn't mix models with functionality like this
183
+ _request_timestamps_lock: Optional[threading.Lock] = None
185
184
 
186
185
  @field_serializer('wait_until',when_used='always')
187
186
  def serialize_wait_until(self, value:Optional[datetime.datetime]) -> Optional[int]:
@@ -253,6 +252,8 @@ class RateLimitState(SubscriptableBaseModel):
253
252
  You only need to use this if your HTTP requests are not automatically being
254
253
  registered, which happens if http.client.HTTPConnection is not being used.
255
254
  """
255
+ if self._request_timestamps_lock is None:
256
+ self._request_timestamps_lock = threading.Lock()
256
257
  with self._request_timestamps_lock:
257
258
  append_time = datetime.datetime.now(datetime.timezone.utc)
258
259
  if self.previous_request_timestamps is None: