omnata-plugin-runtime 0.11.2a317__py3-none-any.whl → 0.11.4a320__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.
@@ -7,6 +7,7 @@ from typing import Any, Dict, Optional, Literal, List, Union, Tuple
7
7
  from typing_extensions import Self
8
8
  from pydantic import BaseModel, Field, model_validator, computed_field
9
9
  from jinja2 import Environment
10
+ from graphlib import TopologicalSorter
10
11
  from .logging import logger
11
12
 
12
13
  class JsonSchemaProperty(BaseModel):
@@ -271,57 +272,60 @@ class SnowflakeViewColumn(BaseModel):
271
272
  )
272
273
 
273
274
  @classmethod
274
- def order_by_reference(cls,current_stream_name:str,columns:List[Self]) -> List[Self]:
275
+ def order_by_reference(cls, current_stream_name: str, columns: List[Self]) -> List[Self]:
275
276
  """
276
- In some situations, column expressions may reference the alias of another column
277
- This is allowed in Snowflake, as long as the aliased column is defined before it's used in a later column
278
- So we need to sort the columns so that if the name of the column appears (in quotes) in the expression of another column, it is ordered first
277
+ Uses topological sorting to order columns so that if a column references another column,
278
+ the referenced column appears first in the list. This is required by Snowflake when
279
+ column expressions reference the alias of another column.
280
+
281
+ OMNATA_ system columns are always placed at the front of the result.
279
282
  """
280
283
  logger.debug(
281
284
  f"Ordering columns by reference for stream: {current_stream_name} ({len(columns)} columns)"
282
285
  )
283
- # Collect columns to be moved
284
- columns_to_move:List[Self] = []
285
- # Collect Omnata System columns and keep them at the front
286
- omnata_system_columns_start = []
287
- for column in columns[:]:
288
- if column.original_name.startswith("OMNATA_"):
289
- columns.remove(column)
290
- omnata_system_columns_start.append(column)
291
-
286
+
287
+ # Separate OMNATA system columns - they always go first
288
+ omnata_system_columns = []
289
+ regular_columns = []
292
290
  for column in columns:
293
- for other_column in columns:
294
- if column==other_column:
295
- continue
296
- if column.original_name in (other_column.referenced_columns or {}).get(current_stream_name,[]):
297
- if column not in columns_to_move:
298
- logger.debug(
299
- f"Column {column.original_name} references {other_column.original_name}, moving it to the front"
300
- )
301
- columns_to_move.append(column)
302
- # we need to do another pass just on columns_to_move, because they may reference each other
303
- # if any do, they go to the front, otherwise they are appended
304
- columns_to_move_final:List[Self] = []
305
- for column in columns_to_move:
306
- for other_column in columns_to_move:
307
- if column==other_column:
308
- continue
309
- if column.original_name in (other_column.referenced_columns or {}).get(current_stream_name,[]):
310
- if column not in columns_to_move_final:
311
- logger.debug(
312
- f"Second pass: Column {column.original_name} is referenced by {other_column.original_name}, moving it to the front"
313
- )
314
- columns_to_move_final.insert(0, column)
315
- continue
316
- if column not in columns_to_move_final:
317
- columns_to_move_final.append(column)
291
+ if column.original_name.startswith("OMNATA_"):
292
+ omnata_system_columns.append(column)
293
+ else:
294
+ regular_columns.append(column)
295
+
296
+ # Build dependency graph: column_name -> list of columns it depends on
297
+ # (i.e., columns that must appear BEFORE it in the final order)
298
+ graph: Dict[str, List[str]] = {}
299
+ column_by_name: Dict[str, Self] = {}
300
+
301
+ for column in regular_columns:
302
+ column_by_name[column.original_name] = column
303
+ # Initialize with empty dependencies
304
+ graph[column.original_name] = []
305
+
306
+ # Add dependencies from referenced_columns
307
+ if column.referenced_columns:
308
+ referenced_in_current_stream = column.referenced_columns.get(current_stream_name, [])
309
+ for ref_col_name in referenced_in_current_stream:
310
+ # This column depends on ref_col_name, so ref_col_name must come first
311
+ graph[column.original_name].append(ref_col_name)
312
+ logger.debug(
313
+ f"Column {column.original_name} depends on {ref_col_name}"
314
+ )
315
+
316
+ # Use TopologicalSorter to sort the columns
317
+ try:
318
+ ts = TopologicalSorter(graph)
319
+ sorted_column_names = list(ts.static_order())
320
+ except ValueError as e:
321
+ # This would indicate a circular dependency
322
+ raise ValueError(f"Circular dependency detected in column references for stream {current_stream_name}: {e}")
323
+
324
+ # Reconstruct the column list in topological order
325
+ sorted_columns = [column_by_name[name] for name in sorted_column_names if name in column_by_name]
318
326
 
319
- # Move collected columns to the front
320
- columns_to_move_final.reverse()
321
- for column in columns_to_move_final:
322
- columns.remove(column)
323
- columns.insert(0, column)
324
- return omnata_system_columns_start + columns
327
+ # Return OMNATA system columns first, followed by sorted regular columns
328
+ return omnata_system_columns + sorted_columns
325
329
 
326
330
 
327
331
  class SnowflakeViewJoin(BaseModel):
@@ -1858,6 +1858,40 @@ class OmnataPlugin(ABC):
1858
1858
  raise NotImplementedError(
1859
1859
  "Your plugin class must implement the inbound_configuration_form method"
1860
1860
  )
1861
+
1862
+ def outbound_tuning_parameters(
1863
+ self, parameters: OutboundSyncConfigurationParameters
1864
+ ) -> OutboundSyncConfigurationForm:
1865
+ """
1866
+ Returns the form definition for declaring outbound tuning parameters.
1867
+
1868
+ The returned form should consist of static fields with default values that represent the
1869
+ plugin's recommended runtime behaviour. This form is optional and is only rendered when a
1870
+ user opts to override those defaults at sync runtime, so it must be safe to fall back to the
1871
+ provided defaults when no tuning parameters are configured.
1872
+
1873
+ :param OutboundSyncConfigurationParameters parameters the current outbound configuration
1874
+ :return: An OutboundSyncConfigurationForm describing the available tuning parameters
1875
+ :rtype: OutboundSyncConfigurationForm
1876
+ """
1877
+ return OutboundSyncConfigurationForm(fields=[])
1878
+
1879
+ def inbound_tuning_parameters(
1880
+ self, parameters: InboundSyncConfigurationParameters
1881
+ ) -> InboundSyncConfigurationForm:
1882
+ """
1883
+ Returns the form definition for declaring inbound tuning parameters.
1884
+
1885
+ The returned form should consist of static fields with default values that represent the
1886
+ plugin's recommended runtime behaviour. This form is optional and is only rendered when a
1887
+ user opts to override those defaults at sync runtime, so it must be safe to fall back to the
1888
+ provided defaults when no tuning parameters are configured.
1889
+
1890
+ :param InboundSyncConfigurationParameters parameters the current inbound configuration
1891
+ :return: An InboundSyncConfigurationForm describing the available tuning parameters
1892
+ :rtype: InboundSyncConfigurationForm
1893
+ """
1894
+ return InboundSyncConfigurationForm(fields=[])
1861
1895
 
1862
1896
  def inbound_stream_list(
1863
1897
  self, parameters: InboundSyncConfigurationParameters
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omnata-plugin-runtime
3
- Version: 0.11.2a317
3
+ Version: 0.11.4a320
4
4
  Summary: Classes and common runtime components for building and running Omnata Plugins
5
5
  License-File: LICENSE
6
6
  Author: James Weakley
@@ -2,12 +2,12 @@ omnata_plugin_runtime/__init__.py,sha256=MS9d1whnfT_B3-ThqZ7l63QeC_8OEKTuaYV5wTw
2
2
  omnata_plugin_runtime/api.py,sha256=5gbjbnFy72Xjf0E3kbG23G0V2J3CorvD5kpBn_BkdlI,8084
3
3
  omnata_plugin_runtime/configuration.py,sha256=SffokJfgvy6V3kUsoEjXcK3GdNgHo6U3mgBEs0qBv4I,46972
4
4
  omnata_plugin_runtime/forms.py,sha256=Lrbr3otsFDrvHWJw7v-slsW4PvEHJ6BG1Yl8oaJfiDo,20529
5
- omnata_plugin_runtime/json_schema.py,sha256=6Hj5SBAW3_moHtCZhVynOUUxCiIIETroCjKFFpPMGJM,54879
5
+ omnata_plugin_runtime/json_schema.py,sha256=HGqqsJGzKT7PSW2re4teyGTiTv-ytEhOSzuvubiz-uY,54826
6
6
  omnata_plugin_runtime/logging.py,sha256=WBuZt8lF9E5oFWM4KYQbE8dDJ_HctJ1pN3BHwU6rcd0,4461
7
- omnata_plugin_runtime/omnata_plugin.py,sha256=PV1BzsqI2yi3pd6DBSRc1s1QJ_My-b6pewrB7ad4sms,140256
7
+ omnata_plugin_runtime/omnata_plugin.py,sha256=xqAIxFdb2X4ryK4VetQxI4u4UdMyN2xs4toLHKasIdU,142045
8
8
  omnata_plugin_runtime/plugin_entrypoints.py,sha256=_1pDLov3iQorGmfcae8Sw2bVjxw1vYeowBaKKNzRclQ,32629
9
9
  omnata_plugin_runtime/rate_limiting.py,sha256=qpr5esU4Ks8hMzuMpSR3gLFdor2ZUXYWCjmsQH_K6lQ,25882
10
- omnata_plugin_runtime-0.11.2a317.dist-info/METADATA,sha256=vQ1Z_h_geCijwFZGpPaN9IshKj5rta5U4HB-jdffjJ8,2233
11
- omnata_plugin_runtime-0.11.2a317.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
12
- omnata_plugin_runtime-0.11.2a317.dist-info/licenses/LICENSE,sha256=rGaMQG3R3F5-JGDp_-rlMKpDIkg5n0SI4kctTk8eZSI,56
13
- omnata_plugin_runtime-0.11.2a317.dist-info/RECORD,,
10
+ omnata_plugin_runtime-0.11.4a320.dist-info/METADATA,sha256=aUfwo3Fm2tXpcbXwnSA2eIMj91E266MknaHA3zAAVMg,2233
11
+ omnata_plugin_runtime-0.11.4a320.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
12
+ omnata_plugin_runtime-0.11.4a320.dist-info/licenses/LICENSE,sha256=rGaMQG3R3F5-JGDp_-rlMKpDIkg5n0SI4kctTk8eZSI,56
13
+ omnata_plugin_runtime-0.11.4a320.dist-info/RECORD,,