omnata-plugin-devkit 0.13.0a158__tar.gz → 0.13.1__tar.gz

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.
Files changed (86) hide show
  1. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/PKG-INFO +1 -1
  2. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/pyproject.toml +1 -1
  3. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/development_session.py +188 -120
  4. omnata_plugin_devkit-0.13.1/src/omnata_plugin_devkit/jinja_templates/CHECK_CONNECTION_PROGRESS.sql.jinja +112 -0
  5. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CONFIGURE_APIS.sql.jinja +5 -0
  6. omnata_plugin_devkit-0.13.1/src/omnata_plugin_devkit/jinja_templates/GET_MISSING_APP_PRIVILEGES.sql.jinja +35 -0
  7. omnata_plugin_devkit-0.13.1/src/omnata_plugin_devkit/jinja_templates/LIST_APP_SPECIFICATIONS.sql.jinja +40 -0
  8. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/NETWORK_ADDRESSES.sql.jinja +2 -1
  9. omnata_plugin_devkit-0.13.1/src/omnata_plugin_devkit/jinja_templates/SET_CONNECTION_OBJECTS.sql.jinja +357 -0
  10. omnata_plugin_devkit-0.13.1/src/omnata_plugin_devkit/jinja_templates/SET_EAI_ENABLED.sql.jinja +43 -0
  11. omnata_plugin_devkit-0.13.1/src/omnata_plugin_devkit/jinja_templates/SET_EAI_SPECIFICATION.sql.jinja +61 -0
  12. omnata_plugin_devkit-0.13.1/src/omnata_plugin_devkit/jinja_templates/SET_SI_SPECIFICATION.sql.jinja +69 -0
  13. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/manifest.yml.jinja +5 -0
  14. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/native_app_packaging.py +0 -2
  15. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/plugin_uploader.py +10 -1
  16. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/test_step_definitions.py +416 -186
  17. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/LICENSE +0 -0
  18. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/README.md +0 -0
  19. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/__init__.py +0 -0
  20. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/airbyte_wrapper.py +0 -0
  21. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/cli/__init__.py +0 -0
  22. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/development.ipynb +0 -0
  23. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/initialiser.py +0 -0
  24. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/API_LIMITS.sql.jinja +0 -0
  25. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/ASSIGN_OUTBOUND_TARGET_TYPE.sql.jinja +0 -0
  26. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CONFIGURATION_FORM.sql.jinja +0 -0
  27. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CONNECTION_FORM.sql.jinja +0 -0
  28. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CONNECTION_TEST.sql.jinja +0 -0
  29. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CONSTRUCT_FORM_OPTION.sql.jinja +0 -0
  30. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CREATE_BILLING_EVENTS.sql.jinja +0 -0
  31. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CREATE_GENERIC_SECRET_OBJECT.sql.jinja +0 -0
  32. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CREATE_GENERIC_SECRET_OBJECT_FROM_EXISTING.sql.jinja +0 -0
  33. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CREATE_NETWORK_RULE_OBJECT.sql.jinja +0 -0
  34. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CREATE_NETWORK_RULE_OBJECT_FROM_EXISTING.sql.jinja +0 -0
  35. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/CREATE_OAUTH_SECRET_OBJECT.sql.jinja +0 -0
  36. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/DROP_NETWORK_RULES.sql.jinja +0 -0
  37. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/DROP_SECRETS.sql.jinja +0 -0
  38. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/FETCH_CONNECTIONS.sql.jinja +0 -0
  39. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/FETCH_SYNCS.sql.jinja +0 -0
  40. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/FETCH_SYNC_BRANCHES.sql.jinja +0 -0
  41. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/INBOUND_LIST_STREAMS.sql.jinja +0 -0
  42. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/LIST_STAGES.sql.jinja +0 -0
  43. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/NGROK_POST_TUNNEL_FIELDS.sql.jinja +0 -0
  44. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/OUTBOUND_RECORD_VALIDATOR.sql.jinja +0 -0
  45. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/PENDING_API_CONFIGURATION.sql.jinja +0 -0
  46. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/POST_INSTALL_ACTIONS.sql.jinja +0 -0
  47. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/RENAME_CONNECTION_METHODS.sql.jinja +0 -0
  48. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/RETRIEVE_NETWORK_RULE_OBJECT.sql.jinja +0 -0
  49. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/RETRIEVE_SECRETS.sql.jinja +0 -0
  50. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/RETRIEVE_SECRETS_UDF.sql.jinja +0 -0
  51. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/SYNC.sql.jinja +0 -0
  52. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/TEST_CALLBACK.sql.jinja +0 -0
  53. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/TEST_OAUTH_TOKEN_EXISTS.sql.jinja +0 -0
  54. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/TUNNEL_TEST.sql.jinja +0 -0
  55. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/UPDATE_API_CONFIGURATION.sql.jinja +0 -0
  56. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/UPDATE_GENERIC_SECRET_OBJECT.sql.jinja +0 -0
  57. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/UPDATE_GENERIC_SECRET_OBJECT_OLD.sql.jinja +0 -0
  58. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/jinja_templates/UPDATE_NETWORK_RULE_OBJECT.sql.jinja +0 -0
  59. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/plugin_registration.py +0 -0
  60. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/plugin_template/icon.svg +0 -0
  61. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/plugin_template/plugin.py +0 -0
  62. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/plugin_template/plugin_development.ipynb +0 -0
  63. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/plugin_template/requirements.txt +0 -0
  64. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/__init__.py +0 -0
  65. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/__init__.py +0 -0
  66. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/api/__init__.py +0 -0
  67. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/api/config.py +0 -0
  68. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/api/constants.py +0 -0
  69. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/api/exceptions.py +0 -0
  70. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/api/secure_path.py +0 -0
  71. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/api/secure_utils.py +0 -0
  72. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/api/utils/__init__.py +0 -0
  73. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/api/utils/types.py +0 -0
  74. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/app/__init__.py +0 -0
  75. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/app/snow_connector.py +0 -0
  76. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/plugins/__init__.py +0 -0
  77. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/plugins/snowpark/__init__.py +0 -0
  78. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/plugins/snowpark/models.py +0 -0
  79. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/plugins/snowpark/package/__init__.py +0 -0
  80. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/plugins/snowpark/package_utils.py +0 -0
  81. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/plugins/snowpark/snowpark_shared.py +0 -0
  82. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/plugins/snowpark/venv.py +0 -0
  83. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/plugins/snowpark/zipper.py +0 -0
  84. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/snowcli/cli/templates/environment.yml.jinja +0 -0
  85. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/streamlit/plugin_configuration.py +0 -0
  86. {omnata_plugin_devkit-0.13.0a158 → omnata_plugin_devkit-0.13.1}/src/omnata_plugin_devkit/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omnata-plugin-devkit
3
- Version: 0.13.0a158
3
+ Version: 0.13.1
4
4
  Summary:
5
5
  License-File: LICENSE
6
6
  Author: James Weakley
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "omnata-plugin-devkit"
3
- version = "0.13.0a158"
3
+ version = "0.13.1"
4
4
  description = ""
5
5
  authors = ["James Weakley <james.weakley@omnata.com>"]
6
6
  readme = "README.md"
@@ -28,6 +28,7 @@ from omnata_plugin_runtime.omnata_plugin import (
28
28
  InboundSyncRequest,
29
29
  OmnataPlugin,
30
30
  OutboundSyncRequest,
31
+ RecordsToUploadResult,
31
32
  SyncRequest,
32
33
  )
33
34
  from omnata_plugin_runtime.logging import OmnataPluginLogHandler
@@ -103,7 +104,7 @@ class SyncScenario:
103
104
  )
104
105
  os.makedirs(os.path.dirname(self.inbound_results_file), exist_ok=True)
105
106
  self.scenario_records = scenario_records
106
- self.results:Optional[pandas.DataFrame] = None
107
+ self.results: Optional[pandas.DataFrame] = None
107
108
  self.initial_stream_state = initial_stream_state
108
109
 
109
110
 
@@ -136,9 +137,9 @@ class DevelopmentSession:
136
137
  self.vcr = None
137
138
  self.recorded_http = recorded_http
138
139
  self.inject_snowpark_session = inject_snowpark_session
139
- self.stream_index_fields:Dict[str,str] = {}
140
- self.stream_excluded_fields:Dict[str,List[str]] = {}
141
- self.exclude_records_by_field_value:Dict[str,str] = {}
140
+ self.stream_index_fields: Dict[str, str] = {}
141
+ self.stream_excluded_fields: Dict[str, List[str]] = {}
142
+ self.exclude_records_by_field_value: Dict[str, str] = {}
142
143
  self.plugin_id = plugin_instance.get_manifest().plugin_id
143
144
  self.feature_filename = os.path.join("features", f"{self.plugin_id}.feature")
144
145
  self.current_scenario = None
@@ -153,8 +154,8 @@ class DevelopmentSession:
153
154
  Collects the full set of records which were previously staged
154
155
  """
155
156
  return self.current_scenario.scenario_records
156
-
157
- def set_inbound_stream_index_field(self,stream_name:str,index_field:str):
157
+
158
+ def set_inbound_stream_index_field(self, stream_name: str, index_field: str):
158
159
  """
159
160
  Sets the field to be used as the index field for a given stream.
160
161
  By default, it is set to the primary key field of the stream, but this may
@@ -163,18 +164,22 @@ class DevelopmentSession:
163
164
  """
164
165
  self.stream_index_fields[stream_name] = index_field
165
166
 
166
- def exclude_inbound_result_fields(self, stream_name: str, field_names: Union[str,List[str]]):
167
+ def exclude_inbound_result_fields(
168
+ self, stream_name: str, field_names: Union[str, List[str]]
169
+ ):
167
170
  """
168
171
  Excludes a field from the comparison of inbound results.
169
172
  """
170
173
  if stream_name not in self.stream_excluded_fields:
171
174
  self.stream_excluded_fields[stream_name] = []
172
- if isinstance(field_names,str):
175
+ if isinstance(field_names, str):
173
176
  self.stream_excluded_fields[stream_name].append(field_names)
174
177
  else:
175
178
  self.stream_excluded_fields[stream_name].extend(field_names)
176
-
177
- def exclude_inbound_result_records_by_field_value(self, field_name: str, field_value: str):
179
+
180
+ def exclude_inbound_result_records_by_field_value(
181
+ self, field_name: str, field_value: str
182
+ ):
178
183
  """
179
184
  Excludes records from the comparison of inbound results where the field matches the value.
180
185
  """
@@ -235,13 +240,13 @@ class DevelopmentSession:
235
240
  if not click.globals.get_current_context(silent=True):
236
241
  # set an empty one so snowcli's app config doesn't error
237
242
  click.globals.push_context(click.core.Context(click.core.Group()))
238
- self.session: Session = get_snowpark_session(connection_name=snowcli_environment)
243
+ self.session: Session = get_snowpark_session(
244
+ connection_name=snowcli_environment
245
+ )
239
246
  self.sync_slug = sync_slug
240
- session_run_response = self.session.sql(
241
- f"""
247
+ session_run_response = self.session.sql(f"""
242
248
  call {self.omnata_app_name}.API.CREATE_DEVELOPMENT_SESSION_RUN('{sync_slug}')
243
- """
244
- ).collect()
249
+ """).collect()
245
250
  response_parsed = json.loads(session_run_response[0][0])
246
251
  if response_parsed["success"] is False:
247
252
  raise ValueError(f"Failed: {response_parsed['error']}")
@@ -276,16 +281,15 @@ class DevelopmentSession:
276
281
  else "null"
277
282
  )
278
283
 
279
- secrets_fetch_response = self.session.sql(
280
- f"""
284
+ secrets_fetch_response = self.session.sql(f"""
281
285
  call {plugin_app_name}.PLUGIN.RETRIEVE_SECRETS({oauth_secret_param},{other_secrets_param})
282
- """
283
- ).collect()
286
+ """).collect()
284
287
  secrets_response_parsed = json.loads(secrets_fetch_response[0][0])
285
288
  if secrets_response_parsed["success"] is False:
286
289
  raise ValueError(f"Failed: {secrets_response_parsed['error']}")
287
- secrets_data = TypeAdapter(
288
- Dict[str, StoredConfigurationValue]).validate_python(secrets_response_parsed["data"])
290
+ secrets_data = TypeAdapter(Dict[str, StoredConfigurationValue]).validate_python(
291
+ secrets_response_parsed["data"]
292
+ )
289
293
 
290
294
  source_database = self.omnata_app_name
291
295
  source_schema = outbound_payload.records_schema_name
@@ -298,17 +302,20 @@ class DevelopmentSession:
298
302
  configuration_parameters = OutboundSyncConfigurationParameters(
299
303
  connection_method=outbound_payload.connection_method,
300
304
  connection_parameters=TypeAdapter(
301
- Dict[str, StoredConfigurationValue]).validate_python(
305
+ Dict[str, StoredConfigurationValue]
306
+ ).validate_python(
302
307
  outbound_payload.connection_parameters,
303
308
  ),
304
309
  connection_secrets=TypeAdapter(
305
- Dict[str, StoredConfigurationValue]).validate_python(secrets_data
306
- ),
310
+ Dict[str, StoredConfigurationValue]
311
+ ).validate_python(secrets_data),
307
312
  sync_parameters=TypeAdapter(
308
- Dict[str, StoredConfigurationValue]).validate_python(outbound_payload.sync_parameters
309
- ),
313
+ Dict[str, StoredConfigurationValue]
314
+ ).validate_python(outbound_payload.sync_parameters),
310
315
  sync_strategy=outbound_payload.sync_strategy,
311
- field_mappings=TypeAdapter(StoredMappingValue).validate_python(outbound_payload.field_mappings),
316
+ field_mappings=TypeAdapter(StoredMappingValue).validate_python(
317
+ outbound_payload.field_mappings
318
+ ),
312
319
  current_form_parameters={},
313
320
  )
314
321
  outbound_sync_request = OutboundSyncRequest(
@@ -323,7 +330,8 @@ class DevelopmentSession:
323
330
  api_limits=self.api_limits,
324
331
  rate_limit_state_all={},
325
332
  rate_limit_state_this_sync_and_branch={},
326
- run_deadline=datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=4),
333
+ run_deadline=datetime.datetime.now(datetime.timezone.utc)
334
+ + datetime.timedelta(hours=4),
327
335
  development_mode=True,
328
336
  )
329
337
 
@@ -359,20 +367,20 @@ class DevelopmentSession:
359
367
  if not click.globals.get_current_context(silent=True):
360
368
  # set an empty one so snowcli's app config doesn't error
361
369
  click.globals.push_context(click.core.Context(click.core.Group()))
362
-
363
- self.session: Session = get_snowpark_session(connection_name=snowcli_environment)
370
+
371
+ self.session: Session = get_snowpark_session(
372
+ connection_name=snowcli_environment
373
+ )
364
374
  # we set the current database to the plugin app name, if it's set in the environment
365
375
  # this means any plugins which call their own UDFs will work
366
- if 'PLUGIN_APP_NAME' in os.environ:
367
- plugin_app_name = os.environ['PLUGIN_APP_NAME']
376
+ if "PLUGIN_APP_NAME" in os.environ:
377
+ plugin_app_name = os.environ["PLUGIN_APP_NAME"]
368
378
  self.session.sql(f"USE DATABASE {plugin_app_name}").collect()
369
379
 
370
380
  self.sync_slug = sync_slug
371
- session_run_response = self.session.sql(
372
- f"""
381
+ session_run_response = self.session.sql(f"""
373
382
  call {self.omnata_app_name}.API.CREATE_DEVELOPMENT_SESSION_RUN('{sync_slug}')
374
- """
375
- ).collect()
383
+ """).collect()
376
384
  response_parsed = json.loads(session_run_response[0][0])
377
385
  if response_parsed["success"] is False:
378
386
  raise ValueError(f"Failed: {response_parsed['error']}")
@@ -408,31 +416,31 @@ class DevelopmentSession:
408
416
  else "null"
409
417
  )
410
418
 
411
- secrets_fetch_response = self.session.sql(
412
- f"""
419
+ secrets_fetch_response = self.session.sql(f"""
413
420
  call {plugin_app_name}.PLUGIN.RETRIEVE_SECRETS({oauth_secret_param},{other_secrets_param})
414
- """
415
- ).collect()
421
+ """).collect()
416
422
  secrets_response_parsed = json.loads(secrets_fetch_response[0][0])
417
423
  if secrets_response_parsed["success"] is False:
418
424
  raise ValueError(f"Failed: {secrets_response_parsed['error']}")
419
- secrets_data = TypeAdapter(
420
- Dict[str, StoredConfigurationValue]).validate_python(secrets_response_parsed["data"])
425
+ secrets_data = TypeAdapter(Dict[str, StoredConfigurationValue]).validate_python(
426
+ secrets_response_parsed["data"]
427
+ )
421
428
 
422
429
  # full_source_table_name = f"{source_database}.{source_schema}.{source_table}"
423
430
  # self.api_limits.concurrency = 1 # we have to set this because pyvcr isn't thread safe
424
431
  configuration_parameters = InboundSyncConfigurationParameters(
425
432
  connection_method=inbound_payload.connection_method,
426
433
  connection_parameters=TypeAdapter(
427
- Dict[str, StoredConfigurationValue]).validate_python(
434
+ Dict[str, StoredConfigurationValue]
435
+ ).validate_python(
428
436
  inbound_payload.connection_parameters,
429
437
  ),
430
438
  connection_secrets=TypeAdapter(
431
- Dict[str, StoredConfigurationValue]).validate_python(secrets_data
432
- ),
439
+ Dict[str, StoredConfigurationValue]
440
+ ).validate_python(secrets_data),
433
441
  sync_parameters=TypeAdapter(
434
- Dict[str, StoredConfigurationValue]).validate_python(inbound_payload.sync_parameters
435
- ),
442
+ Dict[str, StoredConfigurationValue]
443
+ ).validate_python(inbound_payload.sync_parameters),
436
444
  current_form_parameters={},
437
445
  )
438
446
 
@@ -446,7 +454,9 @@ class DevelopmentSession:
446
454
  )
447
455
  for stream in streams_list:
448
456
  if stream.stream.source_defined_primary_key is not None:
449
- self.stream_index_fields[stream.stream_name] = stream.stream.source_defined_primary_key
457
+ self.stream_index_fields[stream.stream_name] = (
458
+ stream.stream.source_defined_primary_key
459
+ )
450
460
  omnata_log_handler = OmnataPluginLogHandler(
451
461
  session=self.session,
452
462
  sync_id=None,
@@ -464,12 +474,13 @@ class DevelopmentSession:
464
474
  api_limits=self.api_limits,
465
475
  rate_limit_state_all={},
466
476
  rate_limit_state_this_sync_and_branch={},
467
- run_deadline=datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=4),
477
+ run_deadline=datetime.datetime.now(datetime.timezone.utc)
478
+ + datetime.timedelta(hours=4),
468
479
  development_mode=True,
469
480
  # this skips over the part where we check for new streams etc,
470
481
  # so development sessions will only get requests for explicitly configured streams
471
482
  streams=streams_list,
472
- omnata_log_handler=omnata_log_handler
483
+ omnata_log_handler=omnata_log_handler,
473
484
  )
474
485
 
475
486
  self.current_scenario = SyncScenario(
@@ -479,14 +490,14 @@ class DevelopmentSession:
479
490
  configuration_parameters=configuration_parameters,
480
491
  sync_request=inbound_sync_request,
481
492
  scenario_records=None,
482
- initial_stream_state=initial_stream_state
493
+ initial_stream_state=initial_stream_state,
483
494
  )
484
495
  self.run_id = inbound_payload.run_id
485
496
  self.plugin_instance._sync_request = inbound_sync_request
486
497
  if self.recorded_http:
487
498
  self._start_recording()
488
499
  return configuration_parameters, inbound_sync_request
489
-
500
+
490
501
  def cancel_scenario(self):
491
502
  """
492
503
  Cancels the current scenario, and stops recording traffic.
@@ -498,7 +509,7 @@ class DevelopmentSession:
498
509
  self._stop_recording()
499
510
  print("Success: cancelled scenario")
500
511
 
501
- def complete_scenario(self,apply_results:bool=True):
512
+ def complete_scenario(self, apply_results: bool = True):
502
513
  """
503
514
  Marks the scenario as complete, and stops recording traffic.
504
515
  """
@@ -511,14 +522,18 @@ class DevelopmentSession:
511
522
  self._stop_recording()
512
523
  if apply_results:
513
524
  self.current_scenario.sync_request.apply_results_queue()
514
- if self.current_scenario.sync_request.__class__.__name__ == "InboundSyncRequest":
515
- print(f'States at completion: {self.current_scenario.sync_request._latest_states}')
516
- self.current_scenario.sync_request.apply_progress_updates(ignore_errors=False)
517
- session_run_response = self.session.sql(
518
- f"""
525
+ if (
526
+ self.current_scenario.sync_request.__class__.__name__
527
+ == "InboundSyncRequest"
528
+ ):
529
+ # Checkpointing have removed this field
530
+ # print(f"States at completion: {self.current_scenario.sync_request._latest_states}")
531
+ self.current_scenario.sync_request.apply_progress_updates(
532
+ ignore_errors=False
533
+ )
534
+ session_run_response = self.session.sql(f"""
519
535
  call {self.omnata_app_name}.API.COMPLETE_DEVELOPMENT_SESSION_RUN({self.run_id},{apply_results})
520
- """
521
- ).collect()
536
+ """).collect()
522
537
  response_parsed = json.loads(session_run_response[0][0])
523
538
  if response_parsed["success"] is False:
524
539
  raise ValueError(f"Failed: {response_parsed['error']}")
@@ -576,7 +591,9 @@ class DevelopmentSession:
576
591
  if self.current_scenario is None:
577
592
  print("Failed: no active scenario")
578
593
  return
579
- results_dataframe = pandas.concat(outbound_sync_request._apply_results).copy()
594
+ results_dataframe = pandas.concat(
595
+ outbound_sync_request._apply_results
596
+ ).copy()
580
597
 
581
598
  originally_provided_records = self.current_scenario.scenario_records
582
599
 
@@ -625,7 +642,10 @@ class DevelopmentSession:
625
642
  print("Success: results are valid")
626
643
  elif sync_request.__class__.__name__ == "InboundSyncRequest":
627
644
  inbound_sync_request: InboundSyncRequest = sync_request
628
- if inbound_sync_request._apply_results is None or len(inbound_sync_request._apply_results) == 0:
645
+ if (
646
+ inbound_sync_request._apply_results is None
647
+ or len(inbound_sync_request._apply_results) == 0
648
+ ):
629
649
  # if there are no results, we check the real results table
630
650
  full_table_name = inbound_sync_request._full_results_table_name
631
651
  # usually if results are provided in a table, RECORD_OBJECT (object) is used instead of RECORD_DATA (varchar)
@@ -637,27 +657,37 @@ class DevelopmentSession:
637
657
  COALESCE(PARSE_JSON(RECORD_DATA), RECORD_OBJECT) as RECORD_OBJECT,
638
658
  IS_DELETED
639
659
  from {full_table_name}""").collect()
640
-
660
+
641
661
  if len(results_from_table) == 0:
642
- print(f"Failed: No results were enqueued to the sync request, and no results found in {full_table_name}")
662
+ print(
663
+ f"Failed: No results were enqueued to the sync request, and no results found in {full_table_name}"
664
+ )
643
665
  else:
644
666
  results_dataframe = pandas.DataFrame(results_from_table)
645
- results_dataframe["RECORD_OBJECT"] = results_dataframe["RECORD_OBJECT"].apply(
646
- lambda x: json.loads(x) if x is not None else None
647
- )
667
+ results_dataframe["RECORD_OBJECT"] = results_dataframe[
668
+ "RECORD_OBJECT"
669
+ ].apply(lambda x: json.loads(x) if x is not None else None)
648
670
  self.current_scenario.results = results_dataframe
649
671
  print("Success: results are valid (retrieved from table)")
650
672
  else:
651
673
  # inbound_sync_request._apply_results is a dictionary of stream_name -> list of dataframes
652
674
  # we want to flatten this into a single dataframe
675
+
676
+ # In Checkpointing, we need to loop each dataframe again as each dataframe holds a RecordsToUploadResult object
653
677
  results_dataframe = pandas.concat(
654
- [df for df_list in inbound_sync_request._apply_results.values() for df in df_list]
678
+ [
679
+ item.records
680
+ for df_list in inbound_sync_request._apply_results.values()
681
+ for item in df_list
682
+ if isinstance(item, RecordsToUploadResult)
683
+ ]
655
684
  ).copy()
685
+
656
686
  # typically, the results table will have a RECORD_DATA column, which is a varchar
657
687
  # we need to parse this as JSON into an object column named RECORD_OBJECT, allowing for nulls
658
- results_dataframe["RECORD_OBJECT"] = results_dataframe["RECORD_DATA"].apply(
659
- lambda x: json.loads(x) if x is not None else None
660
- )
688
+ results_dataframe["RECORD_OBJECT"] = results_dataframe[
689
+ "RECORD_DATA"
690
+ ].apply(lambda x: json.loads(x) if x is not None else None)
661
691
  # we can drop the RECORD_DATA column now
662
692
  results_dataframe = results_dataframe.drop(columns=["RECORD_DATA"])
663
693
  self.current_scenario.results = results_dataframe
@@ -766,20 +796,18 @@ class DevelopmentSession:
766
796
  {
767
797
  "Property": "connection_parameters",
768
798
  "Value": json.dumps(
769
- to_jsonable_python(outbound_params.connection_parameters)
799
+ to_jsonable_python(
800
+ outbound_params.connection_parameters
801
+ )
770
802
  ),
771
803
  },
772
804
  {
773
805
  "Property": "connection_secrets",
774
- "Value": json.dumps(
775
- to_jsonable_python(secrets_redacted)
776
- ),
806
+ "Value": json.dumps(to_jsonable_python(secrets_redacted)),
777
807
  },
778
808
  {
779
809
  "Property": "api_limits",
780
- "Value": json.dumps(
781
- to_jsonable_python(self.api_limits)
782
- ),
810
+ "Value": json.dumps(to_jsonable_python(self.api_limits)),
783
811
  },
784
812
  {
785
813
  "Property": "sync_parameters",
@@ -816,15 +844,11 @@ class DevelopmentSession:
816
844
  },
817
845
  {
818
846
  "Property": "connection_secrets",
819
- "Value": json.dumps(
820
- to_jsonable_python(secrets_redacted)
821
- ),
847
+ "Value": json.dumps(to_jsonable_python(secrets_redacted)),
822
848
  },
823
849
  {
824
850
  "Property": "api_limits",
825
- "Value": json.dumps(
826
- to_jsonable_python(self.api_limits)
827
- ),
851
+ "Value": json.dumps(to_jsonable_python(self.api_limits)),
828
852
  },
829
853
  {
830
854
  "Property": "sync_parameters",
@@ -846,7 +870,11 @@ class DevelopmentSession:
846
870
  f.write(f"\tScenario: {recorded_scenario.scenario_name}\n")
847
871
  if recorded_scenario.sync_direction == SyncDirection.OUTBOUND:
848
872
  f.write("\t\tGiven the following records:\n")
849
- for col in ["RESULT","TRANSFORMED_RECORD", "TRANSFORMED_RECORD_PREVIOUS"]:
873
+ for col in [
874
+ "RESULT",
875
+ "TRANSFORMED_RECORD",
876
+ "TRANSFORMED_RECORD_PREVIOUS",
877
+ ]:
850
878
  recorded_scenario.scenario_records[col] = (
851
879
  recorded_scenario.scenario_records[col]
852
880
  .apply(lambda x: json.loads(x) if x is not None else None)
@@ -859,7 +887,14 @@ class DevelopmentSession:
859
887
  )
860
888
  table_indented = self.dataframe_to_behave_table(
861
889
  recorded_scenario.scenario_records[
862
- ["IDENTIFIER", "APP_IDENTIFIER", "RESULT", "SYNC_ACTION", "TRANSFORMED_RECORD","TRANSFORMED_RECORD_PREVIOUS"]
890
+ [
891
+ "IDENTIFIER",
892
+ "APP_IDENTIFIER",
893
+ "RESULT",
894
+ "SYNC_ACTION",
895
+ "TRANSFORMED_RECORD",
896
+ "TRANSFORMED_RECORD_PREVIOUS",
897
+ ]
863
898
  ],
864
899
  3,
865
900
  )
@@ -871,7 +906,10 @@ class DevelopmentSession:
871
906
  recorded_scenario.sync_request
872
907
  )
873
908
  for stream in inbound_sync_request.streams:
874
- if recorded_scenario.initial_stream_state is not None and stream.stream_name in recorded_scenario.initial_stream_state:
909
+ if (
910
+ recorded_scenario.initial_stream_state is not None
911
+ and stream.stream_name in recorded_scenario.initial_stream_state
912
+ ):
875
913
  f.write(
876
914
  f"\t\t\t| {stream.stream_name} | {json.dumps(recorded_scenario.initial_stream_state[stream.stream_name],separators=(',', ':'),default=str)} | \n"
877
915
  )
@@ -892,9 +930,7 @@ class DevelopmentSession:
892
930
  f"\t\tAnd we use the {self.plugin_instance.__class__.__name__} class from the {self.plugin_module} module\n"
893
931
  )
894
932
  if self.inject_snowpark_session:
895
- f.write(
896
- f"\t\tAnd we attach a Snowpark session to the sync request\n"
897
- )
933
+ f.write(f"\t\tAnd we attach a Snowpark session to the sync request\n")
898
934
  f.write(
899
935
  f"\t\tAnd the plugin returns inbound sync results directly to a real table\n"
900
936
  )
@@ -907,7 +943,9 @@ class DevelopmentSession:
907
943
  "\t\tWhen we perform an inbound sync with configuration parameters:\n"
908
944
  )
909
945
  f.write(f"{table_indented}\n")
910
- f.write(f"\t\tAnd we use the recorded results from {recorded_scenario.inbound_results_file_name}\n")
946
+ f.write(
947
+ f"\t\tAnd we use the recorded results from {recorded_scenario.inbound_results_file_name}\n"
948
+ )
911
949
  if recorded_scenario.sync_direction == SyncDirection.OUTBOUND:
912
950
  f.write("\t\tAnd the response will be:\n")
913
951
  # self.create_results['TRANSFORMED_RECORD']=self.create_results['TRANSFORMED_RECORD'].apply(json.dumps)
@@ -942,9 +980,7 @@ class DevelopmentSession:
942
980
  "RECORD_OBJECT",
943
981
  ].apply(
944
982
  lambda x: {
945
- k: v
946
- for k, v in x.items()
947
- if k not in excluded_fields
983
+ k: v for k, v in x.items() if k not in excluded_fields
948
984
  }
949
985
  )
950
986
  f.write(
@@ -952,56 +988,88 @@ class DevelopmentSession:
952
988
  )
953
989
  # any records excluded by field value are removed both in the recording and the test
954
990
  if len(self.exclude_records_by_field_value) > 0:
955
- for field_name, field_value in self.exclude_records_by_field_value.items():
956
- if field_value == 'true':
991
+ for (
992
+ field_name,
993
+ field_value,
994
+ ) in self.exclude_records_by_field_value.items():
995
+ if field_value == "true":
957
996
  field_value = True
958
- elif field_value == 'false':
997
+ elif field_value == "false":
959
998
  field_value = False
960
- recorded_scenario.results["value_test"] = recorded_scenario.results["RECORD_OBJECT"].apply(
961
- lambda x: x[field_name] if x is not None and field_name in x else None
999
+ recorded_scenario.results[
1000
+ "value_test"
1001
+ ] = recorded_scenario.results["RECORD_OBJECT"].apply(
1002
+ lambda x: (
1003
+ x[field_name]
1004
+ if x is not None and field_name in x
1005
+ else None
1006
+ )
962
1007
  )
963
- matching_records = recorded_scenario.results[recorded_scenario.results["value_test"] == field_value]
964
- print(f"Excluded {len(matching_records)} records where {field_name} is {field_value}")
965
- recorded_scenario.results = recorded_scenario.results[recorded_scenario.results["value_test"] != field_value]
1008
+ matching_records = recorded_scenario.results[
1009
+ recorded_scenario.results["value_test"] == field_value
1010
+ ]
1011
+ print(
1012
+ f"Excluded {len(matching_records)} records where {field_name} is {field_value}"
1013
+ )
1014
+ recorded_scenario.results = recorded_scenario.results[
1015
+ recorded_scenario.results["value_test"] != field_value
1016
+ ]
966
1017
  # remove the temporary column
967
- recorded_scenario.results.drop(columns=["value_test"],inplace=True)
968
-
1018
+ recorded_scenario.results.drop(
1019
+ columns=["value_test"], inplace=True
1020
+ )
969
1021
 
970
1022
  # convert the RECORD_OBJECT to a json string without newlines
971
1023
  recorded_scenario.results["RECORD_OBJECT"] = recorded_scenario.results[
972
1024
  "RECORD_OBJECT"
973
1025
  ].apply(json.dumps)
974
1026
  # also, drop the RETRIEVE_DATE column
975
- recorded_scenario.results.drop(columns=["RETRIEVE_DATE"],inplace=True)
1027
+ recorded_scenario.results.drop(columns=["RETRIEVE_DATE"], inplace=True)
976
1028
  # create the results file if it doesn't exist
977
- os.makedirs(os.path.dirname(recorded_scenario.inbound_results_file), exist_ok=True)
978
- recorded_scenario.results.to_csv(recorded_scenario.inbound_results_file, sep="|", index=False)
1029
+ os.makedirs(
1030
+ os.path.dirname(recorded_scenario.inbound_results_file),
1031
+ exist_ok=True,
1032
+ )
1033
+ recorded_scenario.results.to_csv(
1034
+ recorded_scenario.inbound_results_file, sep="|", index=False
1035
+ )
979
1036
 
980
1037
  if len(self.exclude_records_by_field_value) > 0:
981
- for field_name, field_value in self.exclude_records_by_field_value.items():
982
- f.write(f"\t\tAnd we exclude all inbound results where the field {field_name} has the value {field_value}\n")
1038
+ for (
1039
+ field_name,
1040
+ field_value,
1041
+ ) in self.exclude_records_by_field_value.items():
1042
+ f.write(
1043
+ f"\t\tAnd we exclude all inbound results where the field {field_name} has the value {field_value}\n"
1044
+ )
983
1045
  # group the results by stream name
984
1046
  stream_names = recorded_scenario.results["STREAM_NAME"].unique()
985
1047
 
986
1048
  for stream_name in stream_names:
987
1049
  if stream_name not in self.stream_index_fields:
988
- print(f"Failed: no index field set for stream {stream_name}. Use the set_inbound_stream_index_field function to set one")
1050
+ print(
1051
+ f"Failed: no index field set for stream {stream_name}. Use the set_inbound_stream_index_field function to set one"
1052
+ )
989
1053
  return
990
1054
  f.write(
991
1055
  f"\t\tAnd when indexed on the {self.stream_index_fields[stream_name]} RECORD_OBJECT field, we assert that the recorded results for the {stream_name} stream are correct\n"
992
1056
  )
993
1057
  # count the number of records in the results that have a STREAM_NAME of stream_name
994
- stream_results = recorded_scenario.results[recorded_scenario.results["STREAM_NAME"] == stream_name]
1058
+ stream_results = recorded_scenario.results[
1059
+ recorded_scenario.results["STREAM_NAME"] == stream_name
1060
+ ]
995
1061
  if len(stream_results) == 0:
996
- raise ValueError(f"Recorded results for scenario {recorded_scenario.scenario_name} did not contain any results for stream {stream_name}")
997
- #results = pandas.concat(recorded_scenario.results[stream_name])
1062
+ raise ValueError(
1063
+ f"Recorded results for scenario {recorded_scenario.scenario_name} did not contain any results for stream {stream_name}"
1064
+ )
1065
+ # results = pandas.concat(recorded_scenario.results[stream_name])
998
1066
  ## no need to do this anymore, as the results are already json
999
1067
  ##results["RECORD_DATA"] = results["RECORD_DATA"].apply(json.dumps)
1000
- #results["RECORD_DATA"] = results["RECORD_DATA"].replace("|", "\\|")
1001
- #results.loc[
1068
+ # results["RECORD_DATA"] = results["RECORD_DATA"].replace("|", "\\|")
1069
+ # results.loc[
1002
1070
  # results["RECORD_DATA"] == '"null"', "RECORD_DATA"
1003
- #] = "null"
1004
- #results = results[
1071
+ # ] = "null"
1072
+ # results = results[
1005
1073
  # results.columns.intersection(
1006
1074
  # [
1007
1075
  # "APP_IDENTIFIER",
@@ -1010,9 +1078,9 @@ class DevelopmentSession:
1010
1078
  # "STREAM_NAME",
1011
1079
  # ]
1012
1080
  # )
1013
- #]
1014
- #table_indented = self.dataframe_to_behave_table(results, 3)
1015
- #f.write(f"{table_indented}\n")
1081
+ # ]
1082
+ # table_indented = self.dataframe_to_behave_table(results, 3)
1083
+ # f.write(f"{table_indented}\n")
1016
1084
 
1017
1085
  f.close()
1018
1086
  print("Finished writing feature file")