omnata-plugin-runtime 0.8.3a204__tar.gz → 0.9.0__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.8.3a204
3
+ Version: 0.9.0
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.8.3-a204"
3
+ version = "0.9.0"
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"
@@ -120,6 +120,7 @@ class OutboundSyncRequestPayload(BaseModel):
120
120
  results_table_name: str # used to stage results back to the engine, resides in the main Omnata app database
121
121
  logging_level: str
122
122
  connection_method: str
123
+ target_type: Optional[str] = None
123
124
  connectivity_option: ConnectivityOption = Field(default=ConnectivityOption.DIRECT)
124
125
  connection_parameters: Dict[str, StoredConfigurationValue]
125
126
  oauth_secret_name: Optional[str] = None
@@ -166,6 +167,41 @@ SyncRequestPayload = Annotated[
166
167
  Field(discriminator="sync_direction"),
167
168
  ]
168
169
 
170
+ class OutboundConfigurationFormPayload(BaseModel):
171
+ """
172
+ Encapsulates the payload that is sent to the plugin when it is invoked to provide a configuration form for an outbound sync.
173
+ """
174
+ connectivity_option: ConnectivityOption = Field(default=ConnectivityOption.DIRECT)
175
+ connection_method: str
176
+ connection_parameters: Dict[str, StoredConfigurationValue]
177
+ oauth_secret_name: Optional[str] = None
178
+ other_secrets_name: Optional[str] = None
179
+ sync_direction: Literal["outbound"] = "outbound"
180
+ target_type: Optional[str] = None
181
+ sync_strategy: OutboundSyncStrategy
182
+ function_name: str = "outbound_configuration_form"
183
+ sync_parameters: Dict[str, StoredConfigurationValue]
184
+ current_form_parameters: Optional[Dict[str, StoredConfigurationValue]]
185
+
186
+ class InboundConfigurationFormPayload(BaseModel):
187
+ """
188
+ Encapsulates the payload that is sent to the plugin when it is invoked to provide a configuration form for an inbound sync.
189
+ """
190
+ connectivity_option: ConnectivityOption = Field(default=ConnectivityOption.DIRECT)
191
+ connection_method: str
192
+ connection_parameters: Dict[str, StoredConfigurationValue]
193
+ oauth_secret_name: Optional[str] = None
194
+ other_secrets_name: Optional[str] = None
195
+ sync_direction: Literal["inbound"] = "inbound"
196
+ function_name: str = "inbound_configuration_form"
197
+ sync_parameters: Dict[str, StoredConfigurationValue]
198
+ current_form_parameters: Optional[Dict[str, StoredConfigurationValue]]
199
+
200
+ ConfigurationFormPayload = Annotated[
201
+ Union[OutboundConfigurationFormPayload, InboundConfigurationFormPayload],
202
+ Field(discriminator="sync_direction"),
203
+ ]
204
+
169
205
 
170
206
  def handle_proc_result(query_result: List[Row]) -> Dict:
171
207
  """
@@ -220,6 +220,46 @@ STANDARD_OUTBOUND_SYNC_ACTIONS: Dict[str, OutboundSyncAction] = {
220
220
  "Recreate": RecreateSyncAction,
221
221
  }
222
222
 
223
+ class OutboundTargetParameter(BaseModel):
224
+ """
225
+ Accomodates testing outbound syncs in production by nominating a form field who's value stays in the branch.
226
+ The reason this information is set statically here instead of as a flag on the FormField, is so that the sync engine
227
+ can have this information readily available without calling the plugin.
228
+ """
229
+ field_name: str = Field(title="""The name of the form field that toggles the location, e.g. 'channel','customer_list'.
230
+ This must match a field which will be returned by the outbound_configuration_form for this target type.""")
231
+ is_branching_toggle: bool = Field(title="""Whether or not this field is a target toggle for branching.
232
+ If true, the value of this field will be used to determine the location of the sync in production.
233
+ For example, a messaging plugin could have a "channel" field to route messages to an alternate location.
234
+ Or, a marketing platform could have an alternate customer list name which is connected to test campaigns that don't actually send.
235
+
236
+ This should only be used in situations where all other sync parameters and field mappings can remain consistent between branches.""")
237
+ label: str = Field(title="""Used in the UI when describing the location., e.g. 'Channel','Customer List'.
238
+ It should completely describe the behaviour when used in a sentence like this:
239
+ 'Changes will be tested against a different <label> when running in a branch.'""")
240
+
241
+ class OutboundTargetType(BaseModel):
242
+ """
243
+ Some products have APIs that can be grouped together in ways that support different strategies and may or may not support toggling.
244
+ The label should answer the question: "What would you like to sync to?"
245
+ Examples:
246
+ - A CRM system may have "Standard objects", "Custom objects" or "Events"
247
+ - A messaging platform may have "Channels", "Users" or "Messages"
248
+ - A marketing platform may have "Customer lists", "Campaigns" or "Automations"
249
+ - An Ad platform may have "Campaigns", "Ad groups" or "Ads"
250
+ The target type cannot be changed after the sync is created.
251
+ """
252
+ label: str
253
+ supported_strategies: List[str] = Field(
254
+ title="The names of the sync strategies supported by this target. Each one must match the name of a sync strategy declared in supported_outbound_strategies."
255
+ )
256
+ target_parameter: Optional[OutboundTargetParameter] = Field(
257
+ default=None,
258
+ title="""The sync configuration parameter that designates the target object, if applicable. For example, 'object_name' or 'channel_name'.
259
+ This will be used for two purposes:
260
+ 1. To show a more readable indication of what this sync is doing in the UI, e.g. Standard object: Account
261
+ 2. Designates this field as serving as a br toggle for testing in production.""")
262
+
223
263
 
224
264
  class OutboundSyncStrategy(SubscriptableBaseModel, ABC):
225
265
  """OutboundSyncStrategy is a base class for all outbound sync strategies.
@@ -74,7 +74,8 @@ from .configuration import (
74
74
  SubscriptableBaseModel,
75
75
  SyncConfigurationParameters,
76
76
  get_secrets,
77
- ConnectivityOption
77
+ ConnectivityOption,
78
+ OutboundTargetType,
78
79
  )
79
80
  from .forms import (
80
81
  ConnectionMethod,
@@ -112,45 +113,16 @@ class PluginManifest(SubscriptableBaseModel):
112
113
  docs_url: str
113
114
  supports_inbound: bool
114
115
  supported_outbound_strategies: List[OutboundSyncStrategy] = Field(
115
- ...,
116
- deprecated=True,
117
- title="A list of sync strategies that the plugin can support. This has been replaced by OutboundTargetTypes."
116
+ title="A list of sync strategies that the plugin can support. If outbound_target_types are specified, theses strategies are allocated there. Otherwise, they apply globally"
118
117
  )
119
118
  supported_connectivity_options: List[ConnectivityOption] = Field(
120
119
  default_factory=lambda: [ConnectivityOption.DIRECT]
121
120
  )
122
- outbound_target_types: List[OutboundTargetType] = Field(
123
- ...,
124
- title="A list of target types that the plugin can support. This replaces the deprecated 'supported_outbound_strategies'."
121
+ outbound_target_types: Optional[List[OutboundTargetType]] = Field(
122
+ default=None,
123
+ title="An optional list of target types that the plugin can support."
125
124
  )
126
125
 
127
- class OutboundTargetType(BaseModel):
128
- """
129
- Some products have APIs that can be grouped together in ways that support different strategies and may or may not support toggling.
130
- The label should answer the question: "What would you like to sync to?"
131
- Examples:
132
- - A CRM system may have "Standard objects", "Custom objects" or "Events"
133
- - A messaging platform may have "Channels", "Users" or "Messages"
134
- - A marketing platform may have "Customer lists", "Campaigns" or "Automations"
135
- - An Ad platform may have "Campaigns", "Ad groups" or "Ads"
136
- The target type cannot be changed after the sync is created.
137
- """
138
- label: str
139
- supported_strategies: List[OutboundSyncStrategy]
140
- target_toggle_field: Optional[OutboundTargetToggleField] = None
141
-
142
- class OutboundTargetToggleField(BaseModel):
143
- """
144
- Accomodates testing outbound syncs in production by nominating a form field who's value stays in the branch.
145
- For example, a messaging plugin could have a "channel" field to route messages to an alternate location.
146
- Or, a marketing platform could have an alternate customer list name which is connected to test campaigns that don't actually send.
147
- The field_name must match a field which will be returned by the outbound_configuration_form for this target type.
148
- The reason this is set separately here instead of as a flag on the FormField, is so that the sync engine can know
149
- whether or not location toggling is supported for a target type without calling the plugin.
150
- """
151
- field_name: str = Field(..., title="The name of the form field that toggles the location")
152
- label: str = Field(..., title="Used in the UI when describing the location. It should completely describe the behaviour when used in a sentence like this: 'Changes will be tested against a different <label> when running in a branch.'")
153
-
154
126
  class SnowflakeFunctionParameter(BaseModel):
155
127
  """
156
128
  Represents a parameter for a Snowflake UDF or UDTF
@@ -6,13 +6,13 @@ import os
6
6
  import sys
7
7
  import time
8
8
  import threading
9
- from typing import Dict, List, Optional
9
+ from typing import Dict, List, Optional, cast
10
10
 
11
11
  from pydantic import BaseModel,TypeAdapter # pylint: disable=no-name-in-module
12
12
  from pydantic_core import to_jsonable_python
13
13
  from snowflake.snowpark import Session
14
14
 
15
- from .api import PluginMessageStreamProgressUpdate, SyncRequestPayload, handle_proc_result
15
+ from .api import PluginMessageStreamProgressUpdate, SyncRequestPayload, ConfigurationFormPayload
16
16
  from .configuration import (
17
17
  ConnectionConfigurationParameters,
18
18
  InboundSyncConfigurationParameters,
@@ -83,7 +83,7 @@ class PluginEntrypoint:
83
83
 
84
84
 
85
85
  def sync(self, sync_request: Dict):
86
- request = TypeAdapter(SyncRequestPayload).validate_python(sync_request)
86
+ request:SyncRequestPayload = TypeAdapter(SyncRequestPayload).validate_python(sync_request)
87
87
  logger.add_extra('omnata.operation', 'sync')
88
88
  logger.add_extra('omnata.sync.id', request.sync_id)
89
89
  logger.add_extra('omnata.sync.direction', request.sync_direction)
@@ -160,6 +160,7 @@ class PluginEntrypoint:
160
160
  connection_secrets=connection_secrets,
161
161
  sync_parameters=request.sync_parameters,
162
162
  current_form_parameters={},
163
+ target_type=request.target_type,
163
164
  sync_strategy=request.sync_strategy,
164
165
  field_mappings=request.field_mappings,
165
166
  )
@@ -289,67 +290,43 @@ class PluginEntrypoint:
289
290
  logger.info("Finished applying records")
290
291
  return return_dict
291
292
 
292
- def configuration_form(
293
- self,
294
- connectivity_option:str,
295
- connection_method: str,
296
- connection_parameters: Dict,
297
- oauth_secret_name: Optional[str],
298
- other_secrets_name: Optional[str],
299
- sync_direction: str,
300
- sync_strategy: Dict,
301
- function_name: str,
302
- sync_parameters: Dict,
303
- current_form_parameters: Optional[Dict],
304
- ):
305
- if function_name is None:
306
- function_name = f"{sync_direction}_configuration_form"
293
+ def configuration_form(self, configuration_form_request: Dict):
294
+ request:ConfigurationFormPayload = TypeAdapter(ConfigurationFormPayload).validate_python(configuration_form_request)
307
295
  logger.add_extra('omnata.operation', 'configuration_form')
308
- logger.add_extra('omnata.connection.connectivity_option', connectivity_option)
309
- logger.add_extra('omnata.connection.connection_method', connection_method)
310
- logger.add_extra('omnata.configuration_form.function_name', function_name)
311
- logger.add_extra('omnata.sync.direction', sync_direction)
296
+ logger.add_extra('omnata.connection.connectivity_option', request.connectivity_option)
297
+ logger.add_extra('omnata.connection.connection_method', request.connection_method)
298
+ logger.add_extra('omnata.configuration_form.function_name', request.function_name)
299
+ logger.add_extra('omnata.sync.direction', request.sync_direction)
312
300
 
313
301
  logger.info("Entered configuration_form method")
314
- sync_strategy = normalise_nulls(sync_strategy)
315
- oauth_secret_name = normalise_nulls(oauth_secret_name)
316
- other_secrets_name = normalise_nulls(other_secrets_name)
317
- connection_secrets = get_secrets(oauth_secret_name, other_secrets_name)
318
- connectivity_option = TypeAdapter(ConnectivityOption).validate_python(connectivity_option)
319
- connection_parameters = TypeAdapter(
320
- Dict[str, StoredConfigurationValue]).validate_python(connection_parameters)
321
- sync_parameters = TypeAdapter(
322
- Dict[str, StoredConfigurationValue]).validate_python(sync_parameters)
323
- form_parameters = None
324
- if current_form_parameters is not None:
325
- form_parameters = TypeAdapter(Dict[str, StoredConfigurationValue]).validate_python(current_form_parameters)
326
- if sync_direction == "outbound":
327
- sync_strat = OutboundSyncStrategy.model_validate(sync_strategy) if sync_strategy is not None else None
302
+ connection_secrets = get_secrets(request.oauth_secret_name, request.other_secrets_name)
303
+ if request.sync_direction == "outbound":
328
304
  parameters = OutboundSyncConfigurationParameters(
329
- connection_parameters=connection_parameters,
305
+ connection_parameters=request.connection_parameters,
330
306
  connection_secrets=connection_secrets,
331
- sync_strategy=sync_strat,
332
- sync_parameters=sync_parameters,
333
- connection_method=connection_method,
334
- connectivity_option=connectivity_option,
335
- current_form_parameters=form_parameters,
307
+ sync_strategy=request.sync_strategy,
308
+ sync_parameters=request.sync_parameters,
309
+ connection_method=request.connection_method,
310
+ connectivity_option=request.connectivity_option,
311
+ current_form_parameters=request.current_form_parameters,
312
+ target_type=request.target_type
336
313
  )
337
- elif sync_direction == "inbound":
314
+ elif request.sync_direction == "inbound":
338
315
  parameters = InboundSyncConfigurationParameters(
339
- connection_parameters=connection_parameters,
316
+ connection_parameters=request.connection_parameters,
340
317
  connection_secrets=connection_secrets,
341
- sync_parameters=sync_parameters,
342
- connection_method=connection_method,
343
- connectivity_option=connectivity_option,
344
- current_form_parameters=form_parameters,
318
+ sync_parameters=request.sync_parameters,
319
+ connection_method=request.connection_method,
320
+ connectivity_option=request.connectivity_option,
321
+ current_form_parameters=request.current_form_parameters,
345
322
  )
346
323
  else:
347
- raise ValueError(f"Unknown direction {sync_direction}")
348
- if oauth_secret_name is not None:
349
- parameters.access_token_secret_name = oauth_secret_name
324
+ raise ValueError(f"Unknown direction {request.sync_direction}")
325
+ if request.oauth_secret_name is not None:
326
+ parameters.access_token_secret_name = request.oauth_secret_name
350
327
  the_function = getattr(
351
328
  self._plugin_instance,
352
- function_name
329
+ request.function_name
353
330
  )
354
331
  with tracer.start_as_current_span("invoke_plugin"):
355
332
  script_result = the_function(parameters)
@@ -357,6 +334,7 @@ class PluginEntrypoint:
357
334
  script_result = script_result.model_dump()
358
335
  elif isinstance(script_result, List):
359
336
  if len(script_result) > 0 and isinstance(script_result[0], BaseModel):
337
+ script_result = cast(List[BaseModel], script_result)
360
338
  script_result = [r.model_dump() for r in script_result]
361
339
  return script_result
362
340