moose-lib 0.6.90__py3-none-any.whl → 0.6.283__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.
Files changed (59) hide show
  1. moose_lib/__init__.py +38 -3
  2. moose_lib/blocks.py +497 -37
  3. moose_lib/clients/redis_client.py +26 -14
  4. moose_lib/commons.py +94 -5
  5. moose_lib/config/config_file.py +44 -2
  6. moose_lib/config/runtime.py +137 -5
  7. moose_lib/data_models.py +451 -46
  8. moose_lib/dmv2/__init__.py +88 -60
  9. moose_lib/dmv2/_registry.py +3 -1
  10. moose_lib/dmv2/_source_capture.py +37 -0
  11. moose_lib/dmv2/consumption.py +55 -32
  12. moose_lib/dmv2/ingest_api.py +9 -2
  13. moose_lib/dmv2/ingest_pipeline.py +56 -13
  14. moose_lib/dmv2/life_cycle.py +3 -1
  15. moose_lib/dmv2/materialized_view.py +24 -14
  16. moose_lib/dmv2/moose_model.py +165 -0
  17. moose_lib/dmv2/olap_table.py +304 -119
  18. moose_lib/dmv2/registry.py +28 -3
  19. moose_lib/dmv2/sql_resource.py +16 -8
  20. moose_lib/dmv2/stream.py +241 -21
  21. moose_lib/dmv2/types.py +14 -8
  22. moose_lib/dmv2/view.py +13 -6
  23. moose_lib/dmv2/web_app.py +175 -0
  24. moose_lib/dmv2/web_app_helpers.py +96 -0
  25. moose_lib/dmv2/workflow.py +37 -9
  26. moose_lib/internal.py +537 -68
  27. moose_lib/main.py +87 -56
  28. moose_lib/query_builder.py +18 -5
  29. moose_lib/query_param.py +54 -20
  30. moose_lib/secrets.py +122 -0
  31. moose_lib/streaming/streaming_function_runner.py +266 -156
  32. moose_lib/utilities/sql.py +0 -1
  33. {moose_lib-0.6.90.dist-info → moose_lib-0.6.283.dist-info}/METADATA +19 -1
  34. moose_lib-0.6.283.dist-info/RECORD +63 -0
  35. tests/__init__.py +1 -1
  36. tests/conftest.py +38 -1
  37. tests/test_backward_compatibility.py +85 -0
  38. tests/test_cluster_validation.py +85 -0
  39. tests/test_codec.py +75 -0
  40. tests/test_column_formatting.py +80 -0
  41. tests/test_fixedstring.py +43 -0
  42. tests/test_iceberg_config.py +105 -0
  43. tests/test_int_types.py +211 -0
  44. tests/test_kafka_config.py +141 -0
  45. tests/test_materialized.py +74 -0
  46. tests/test_metadata.py +37 -0
  47. tests/test_moose.py +21 -30
  48. tests/test_moose_model.py +153 -0
  49. tests/test_olap_table_moosemodel.py +89 -0
  50. tests/test_olap_table_versioning.py +210 -0
  51. tests/test_query_builder.py +97 -9
  52. tests/test_redis_client.py +10 -3
  53. tests/test_s3queue_config.py +211 -110
  54. tests/test_secrets.py +239 -0
  55. tests/test_simple_aggregate.py +114 -0
  56. tests/test_web_app.py +227 -0
  57. moose_lib-0.6.90.dist-info/RECORD +0 -42
  58. {moose_lib-0.6.90.dist-info → moose_lib-0.6.283.dist-info}/WHEEL +0 -0
  59. {moose_lib-0.6.90.dist-info → moose_lib-0.6.283.dist-info}/top_level.txt +0 -0
@@ -15,6 +15,10 @@ from .types import (
15
15
  ZeroOrMany,
16
16
  )
17
17
 
18
+ from .moose_model import (
19
+ MooseModel,
20
+ )
21
+
18
22
  from .olap_table import (
19
23
  OlapConfig,
20
24
  OlapTable,
@@ -28,6 +32,10 @@ from .stream import (
28
32
  Stream,
29
33
  DeadLetterModel,
30
34
  DeadLetterQueue,
35
+ SubjectLatest,
36
+ SubjectVersion,
37
+ SchemaById,
38
+ KafkaSchemaConfig,
31
39
  )
32
40
 
33
41
  from .ingest_api import (
@@ -76,6 +84,18 @@ from .life_cycle import (
76
84
  LifeCycle,
77
85
  )
78
86
 
87
+ from .web_app import (
88
+ WebApp,
89
+ WebAppConfig,
90
+ WebAppMetadata,
91
+ )
92
+
93
+ from .web_app_helpers import (
94
+ ApiUtil,
95
+ get_moose_utils,
96
+ get_moose_dependency,
97
+ )
98
+
79
99
  from .registry import (
80
100
  get_tables,
81
101
  get_table,
@@ -89,6 +109,8 @@ from .registry import (
89
109
  get_sql_resource,
90
110
  get_workflows,
91
111
  get_workflow,
112
+ get_web_apps,
113
+ get_web_app,
92
114
  # Backward compatibility aliases
93
115
  get_consumption_apis,
94
116
  get_consumption_api,
@@ -96,74 +118,80 @@ from .registry import (
96
118
 
97
119
  __all__ = [
98
120
  # Types
99
- 'BaseTypedResource',
100
- 'TypedMooseResource',
101
- 'Columns',
102
- 'T',
103
- 'U',
104
- 'T_none',
105
- 'U_none',
106
- 'ZeroOrMany',
107
-
121
+ "BaseTypedResource",
122
+ "TypedMooseResource",
123
+ "Columns",
124
+ "MooseModel",
125
+ "T",
126
+ "U",
127
+ "T_none",
128
+ "U_none",
129
+ "ZeroOrMany",
108
130
  # OLAP Tables
109
- 'OlapConfig',
110
- 'OlapTable',
111
- 'InsertOptions',
112
-
131
+ "OlapConfig",
132
+ "OlapTable",
133
+ "InsertOptions",
113
134
  # Streams
114
- 'StreamConfig',
115
- 'TransformConfig',
116
- 'ConsumerConfig',
117
- 'Stream',
118
- 'DeadLetterModel',
119
- 'DeadLetterQueue',
120
-
135
+ "StreamConfig",
136
+ "TransformConfig",
137
+ "ConsumerConfig",
138
+ "Stream",
139
+ "DeadLetterModel",
140
+ "DeadLetterQueue",
141
+ "SubjectLatest",
142
+ "SubjectVersion",
143
+ "SchemaById",
144
+ "KafkaSchemaConfig",
121
145
  # Ingestion
122
- 'IngestConfig',
123
- 'IngestConfigWithDestination',
124
- 'IngestPipelineConfig',
125
- 'IngestApi',
126
- 'IngestPipeline',
127
-
146
+ "IngestConfig",
147
+ "IngestConfigWithDestination",
148
+ "IngestPipelineConfig",
149
+ "IngestApi",
150
+ "IngestPipeline",
128
151
  # Consumption
129
- 'ApiConfig',
130
- 'Api',
131
- 'get_moose_base_url',
132
- 'set_moose_base_url',
152
+ "ApiConfig",
153
+ "Api",
154
+ "get_moose_base_url",
155
+ "set_moose_base_url",
133
156
  # Backward compatibility aliases (deprecated)
134
- 'ConsumptionApi',
135
- 'EgressConfig',
136
-
157
+ "ConsumptionApi",
158
+ "EgressConfig",
137
159
  # SQL
138
- 'SqlResource',
139
- 'View',
140
- 'MaterializedViewOptions',
141
- 'MaterializedView',
142
-
160
+ "SqlResource",
161
+ "View",
162
+ "MaterializedViewOptions",
163
+ "MaterializedView",
143
164
  # Workflow
144
- 'TaskContext',
145
- 'TaskConfig',
146
- 'Task',
147
- 'WorkflowConfig',
148
- 'Workflow',
149
-
165
+ "TaskContext",
166
+ "TaskConfig",
167
+ "Task",
168
+ "WorkflowConfig",
169
+ "Workflow",
150
170
  # Lifecycle
151
- 'LifeCycle',
152
-
171
+ "LifeCycle",
172
+ # WebApp
173
+ "WebApp",
174
+ "WebAppConfig",
175
+ "WebAppMetadata",
176
+ "ApiUtil",
177
+ "get_moose_utils",
178
+ "get_moose_dependency",
153
179
  # Registry
154
- 'get_tables',
155
- 'get_table',
156
- 'get_streams',
157
- 'get_stream',
158
- 'get_ingest_apis',
159
- 'get_ingest_api',
160
- 'get_apis',
161
- 'get_api',
162
- 'get_sql_resources',
163
- 'get_sql_resource',
164
- 'get_workflows',
165
- 'get_workflow',
180
+ "get_tables",
181
+ "get_table",
182
+ "get_streams",
183
+ "get_stream",
184
+ "get_ingest_apis",
185
+ "get_ingest_api",
186
+ "get_apis",
187
+ "get_api",
188
+ "get_sql_resources",
189
+ "get_sql_resource",
190
+ "get_workflows",
191
+ "get_workflow",
192
+ "get_web_apps",
193
+ "get_web_app",
166
194
  # Backward compatibility aliases (deprecated)
167
- 'get_consumption_apis',
168
- 'get_consumption_api',
195
+ "get_consumption_apis",
196
+ "get_consumption_api",
169
197
  ]
@@ -4,6 +4,7 @@ Internal registry dictionaries for Moose Data Model v2 (dmv2) resources.
4
4
  This module maintains the raw dictionaries that store all registered resources.
5
5
  It has no imports from other dmv2 modules to avoid circular dependencies.
6
6
  """
7
+
7
8
  from typing import Dict, Any
8
9
 
9
10
  # Global registries for all resource types
@@ -16,4 +17,5 @@ _api_name_aliases: Dict[str, Any] = {}
16
17
  # Map from custom paths to API instances for path-based lookup
17
18
  _api_path_map: Dict[str, Any] = {}
18
19
  _sql_resources: Dict[str, Any] = {}
19
- _workflows: Dict[str, Any] = {}
20
+ _workflows: Dict[str, Any] = {}
21
+ _web_apps: Dict[str, Any] = {}
@@ -0,0 +1,37 @@
1
+ """
2
+ Utilities for capturing source file information from the call stack.
3
+
4
+ This module provides functions to extract source file paths from Python's
5
+ call stack, filtering out internal library paths.
6
+ """
7
+
8
+ from typing import Optional
9
+ import inspect
10
+
11
+
12
+ def get_source_file_from_stack() -> Optional[str]:
13
+ """Extract the source file path from the call stack, skipping internal modules.
14
+
15
+ Returns the first file path that is not from internal moose_lib modules,
16
+ site-packages, or special Python frames.
17
+
18
+ Returns:
19
+ The absolute path to the user's source file, or None if not found.
20
+ """
21
+ try:
22
+ # Get the current call stack
23
+ stack = inspect.stack()
24
+ # Start from index 1 to skip this function itself
25
+ for frame_info in stack[1:]:
26
+ filename = frame_info.filename
27
+ # Skip internal modules and site-packages
28
+ if (
29
+ "site-packages" not in filename
30
+ and "moose_lib" not in filename
31
+ and "<" not in filename # Skip special frames like <frozen importlib>
32
+ ):
33
+ return filename
34
+ except Exception:
35
+ # If anything goes wrong, just return None
36
+ pass
37
+ return None
@@ -4,6 +4,7 @@ API definitions for Moose Data Model v2 (dmv2).
4
4
  This module provides classes for defining and configuring APIs
5
5
  that allow querying data through user-defined functions.
6
6
  """
7
+
7
8
  import os
8
9
  from typing import Any, Callable, Optional, Tuple, Generic
9
10
 
@@ -17,13 +18,14 @@ from ._registry import _apis, _api_name_aliases, _api_path_map
17
18
  # Global base URL configuration
18
19
  _global_base_url: Optional[str] = None
19
20
 
21
+
20
22
  def _generate_api_key(name: str, version: Optional[str] = None) -> str:
21
23
  return f"{name}:{version}" if version else name
22
24
 
23
25
 
24
26
  def set_moose_base_url(url: str) -> None:
25
27
  """Set the global base URL for API calls.
26
-
28
+
27
29
  Args:
28
30
  url: The base URL to use for API calls
29
31
  """
@@ -33,14 +35,14 @@ def set_moose_base_url(url: str) -> None:
33
35
 
34
36
  def get_moose_base_url() -> Optional[str]:
35
37
  """Get the configured base URL from global setting or environment variable.
36
-
38
+
37
39
  Returns:
38
40
  The base URL if configured, None otherwise
39
41
  """
40
42
  # Priority: programmatically set > environment variable
41
43
  if _global_base_url:
42
44
  return _global_base_url
43
- return os.getenv('MOOSE_BASE_URL')
45
+ return os.getenv("MOOSE_BASE_URL")
44
46
 
45
47
 
46
48
  class ApiConfig(BaseModel):
@@ -51,6 +53,7 @@ class ApiConfig(BaseModel):
51
53
  path: Optional custom path for the API endpoint. If not specified, defaults to the API name.
52
54
  metadata: Optional metadata for the API.
53
55
  """
56
+
54
57
  version: Optional[str] = None
55
58
  path: Optional[str] = None
56
59
  metadata: Optional[dict] = None
@@ -78,6 +81,7 @@ class Api(BaseTypedResource, Generic[U]):
78
81
  model_type (type[T]): The Pydantic model for the input/query parameters.
79
82
  return_type (type[U]): The Pydantic model for the response body.
80
83
  """
84
+
81
85
  config: ApiConfig
82
86
  query_function: Callable[..., U]
83
87
  _u: type[U]
@@ -85,7 +89,9 @@ class Api(BaseTypedResource, Generic[U]):
85
89
  def __class_getitem__(cls, items):
86
90
  # Handle two type parameters
87
91
  if not isinstance(items, tuple) or len(items) != 2:
88
- raise ValueError(f"Use `{cls.__name__}[T, U](name='...')` to supply both input and output types")
92
+ raise ValueError(
93
+ f"Use `{cls.__name__}[T, U](name='...')` to supply both input and output types"
94
+ )
89
95
  input_type, output_type = items
90
96
 
91
97
  def curried_constructor(*args, **kwargs):
@@ -93,19 +99,23 @@ class Api(BaseTypedResource, Generic[U]):
93
99
 
94
100
  return curried_constructor
95
101
 
96
- def __init__(self, name: str, query_function: Callable[..., U], config: ApiConfig = None, version: str = None, **kwargs):
102
+ def __init__(
103
+ self,
104
+ name: str,
105
+ query_function: Callable[..., U],
106
+ config: ApiConfig = None,
107
+ version: str = None,
108
+ **kwargs,
109
+ ):
97
110
  super().__init__()
98
111
  self._set_type(name, self._get_type(kwargs))
99
-
112
+
100
113
  # Handle config and version parameters properly
101
114
  if config is not None:
102
115
  # If config is provided, use it as base
103
116
  if version is not None:
104
117
  # If version is also provided, update the config's version
105
- self.config = ApiConfig(
106
- version=version,
107
- metadata=config.metadata
108
- )
118
+ self.config = ApiConfig(version=version, metadata=config.metadata)
109
119
  else:
110
120
  # Use the provided config as-is
111
121
  self.config = config
@@ -117,7 +127,7 @@ class Api(BaseTypedResource, Generic[U]):
117
127
  self.config = ApiConfig()
118
128
 
119
129
  self.query_function = query_function
120
- self.metadata = getattr(self.config, 'metadata', {}) or {}
130
+ self.metadata = getattr(self.config, "metadata", {}) or {}
121
131
  key = _generate_api_key(name, self.config.version)
122
132
 
123
133
  # Check for duplicate registration
@@ -143,13 +153,15 @@ class Api(BaseTypedResource, Generic[U]):
143
153
  else:
144
154
  # For versioned APIs, check if version is already in the path
145
155
  path_ends_with_version = (
146
- self.config.path.endswith(f"/{self.config.version}") or
147
- self.config.path == self.config.version or
148
- (self.config.path.endswith(self.config.version) and
149
- len(self.config.path) > len(self.config.version) and
150
- self.config.path[-(len(self.config.version) + 1)] == '/')
156
+ self.config.path.endswith(f"/{self.config.version}")
157
+ or self.config.path == self.config.version
158
+ or (
159
+ self.config.path.endswith(self.config.version)
160
+ and len(self.config.path) > len(self.config.version)
161
+ and self.config.path[-(len(self.config.version) + 1)] == "/"
162
+ )
151
163
  )
152
-
164
+
153
165
  if path_ends_with_version:
154
166
  # Path already contains version, check for collision
155
167
  if self.config.path in _api_path_map:
@@ -161,8 +173,10 @@ class Api(BaseTypedResource, Generic[U]):
161
173
  _api_path_map[self.config.path] = self
162
174
  else:
163
175
  # Path doesn't contain version, register with version appended
164
- path_with_version = f"{self.config.path.rstrip('/')}/{self.config.version}"
165
-
176
+ path_with_version = (
177
+ f"{self.config.path.rstrip('/')}/{self.config.version}"
178
+ )
179
+
166
180
  # Check for collision on versioned path
167
181
  if path_with_version in _api_path_map:
168
182
  existing = _api_path_map[path_with_version]
@@ -171,7 +185,7 @@ class Api(BaseTypedResource, Generic[U]):
171
185
  f'this path is already used by API "{existing.name}"'
172
186
  )
173
187
  _api_path_map[path_with_version] = self
174
-
188
+
175
189
  # Also register the unversioned path if not already claimed
176
190
  # (This is intentionally more permissive - first API gets the unversioned path)
177
191
  if self.config.path not in _api_path_map:
@@ -209,9 +223,11 @@ class Api(BaseTypedResource, Generic[U]):
209
223
 
210
224
  @classmethod
211
225
  def _get_type(cls, keyword_args: dict):
212
- t = keyword_args.get('t')
226
+ t = keyword_args.get("t")
213
227
  if not isinstance(t, tuple) or len(t) != 2:
214
- raise ValueError(f"Use `{cls.__name__}[T, U](name='...')` to supply both input and output types")
228
+ raise ValueError(
229
+ f"Use `{cls.__name__}[T, U](name='...')` to supply both input and output types"
230
+ )
215
231
 
216
232
  input_type, output_type = t
217
233
  if not isinstance(input_type, type) or not issubclass(input_type, BaseModel):
@@ -237,21 +253,22 @@ class Api(BaseTypedResource, Generic[U]):
237
253
  A dictionary representing the JSON schema.
238
254
  """
239
255
  from pydantic.type_adapter import TypeAdapter
256
+
240
257
  return TypeAdapter(self.return_type()).json_schema(
241
- ref_template='#/components/schemas/{model}'
258
+ ref_template="#/components/schemas/{model}"
242
259
  )
243
260
 
244
261
  def call(self, params: T, base_url: Optional[str] = None) -> U:
245
262
  """Call the API with the given parameters.
246
-
263
+
247
264
  Args:
248
265
  params: Parameters matching the input model T
249
266
  base_url: Optional base URL override. If not provided, uses the global
250
267
  base URL set via set_base_url() or MOOSE_BASE_URL environment variable.
251
-
268
+
252
269
  Returns:
253
270
  Response data matching the output model U
254
-
271
+
255
272
  Raises:
256
273
  ValueError: If no base URL is configured
257
274
  """
@@ -268,11 +285,13 @@ class Api(BaseTypedResource, Generic[U]):
268
285
  # Check if the custom path already contains the version
269
286
  if self.config.version:
270
287
  path_ends_with_version = (
271
- self.config.path.endswith(f"/{self.config.version}") or
272
- self.config.path == self.config.version or
273
- (self.config.path.endswith(self.config.version) and
274
- len(self.config.path) > len(self.config.version) and
275
- self.config.path[-(len(self.config.version) + 1)] == '/')
288
+ self.config.path.endswith(f"/{self.config.version}")
289
+ or self.config.path == self.config.version
290
+ or (
291
+ self.config.path.endswith(self.config.version)
292
+ and len(self.config.path) > len(self.config.version)
293
+ and self.config.path[-(len(self.config.version) + 1)] == "/"
294
+ )
276
295
  )
277
296
  if path_ends_with_version:
278
297
  path = self.config.path
@@ -282,7 +301,11 @@ class Api(BaseTypedResource, Generic[U]):
282
301
  path = self.config.path
283
302
  else:
284
303
  # Default to name with optional version
285
- path = self.name if not self.config.version else f"{self.name}/{self.config.version}"
304
+ path = (
305
+ self.name
306
+ if not self.config.version
307
+ else f"{self.name}/{self.config.version}"
308
+ )
286
309
  url = f"{effective_base_url.rstrip('/')}/api/{path}"
287
310
 
288
311
  # Convert Pydantic model to dictionary
@@ -4,6 +4,7 @@ Ingestion API definitions for Moose Data Model v2 (dmv2).
4
4
  This module provides classes for defining and configuring ingestion APIs
5
5
  that receive data and send it to streams.
6
6
  """
7
+
7
8
  import dataclasses
8
9
  from typing import Any, Optional, Generic
9
10
  from pydantic import BaseModel
@@ -12,6 +13,7 @@ from .types import TypedMooseResource, T
12
13
  from .stream import Stream, DeadLetterQueue
13
14
  from ._registry import _ingest_apis
14
15
 
16
+
15
17
  class IngestConfig(BaseModel):
16
18
  """Basic configuration for an ingestion point.
17
19
 
@@ -20,10 +22,12 @@ class IngestConfig(BaseModel):
20
22
  path: Optional custom path for the ingestion endpoint.
21
23
  metadata: Optional metadata for the ingestion point.
22
24
  """
25
+
23
26
  version: Optional[str] = None
24
27
  path: Optional[str] = None
25
28
  metadata: Optional[dict] = None
26
29
 
30
+
27
31
  @dataclasses.dataclass
28
32
  class IngestConfigWithDestination[T: BaseModel]:
29
33
  """Ingestion configuration that includes the mandatory destination stream.
@@ -35,12 +39,14 @@ class IngestConfigWithDestination[T: BaseModel]:
35
39
  path: Optional custom path for the ingestion endpoint.
36
40
  metadata: Optional metadata for the ingestion configuration.
37
41
  """
42
+
38
43
  destination: Stream[T]
39
44
  dead_letter_queue: Optional[DeadLetterQueue[T]] = None
40
45
  version: Optional[str] = None
41
46
  path: Optional[str] = None
42
47
  metadata: Optional[dict] = None
43
48
 
49
+
44
50
  class IngestApi(TypedMooseResource, Generic[T]):
45
51
  """Represents an Ingestion API endpoint typed with a Pydantic model.
46
52
 
@@ -59,11 +65,12 @@ class IngestApi(TypedMooseResource, Generic[T]):
59
65
  name (str): The name of the API.
60
66
  model_type (type[T]): The Pydantic model associated with this API's input.
61
67
  """
68
+
62
69
  config: IngestConfigWithDestination[T]
63
70
 
64
71
  def __init__(self, name: str, config: IngestConfigWithDestination[T], **kwargs):
65
72
  super().__init__()
66
73
  self._set_type(name, self._get_type(kwargs))
67
74
  self.config = config
68
- self.metadata = getattr(config, 'metadata', None)
69
- _ingest_apis[name] = self
75
+ self.metadata = getattr(config, "metadata", None)
76
+ _ingest_apis[name] = self
@@ -4,8 +4,10 @@ Ingestion Pipeline definitions for Moose Data Model v2 (dmv2).
4
4
  This module provides classes for defining and configuring complete ingestion pipelines,
5
5
  which combine tables, streams, and ingestion APIs into a single cohesive unit.
6
6
  """
7
+
8
+ import warnings
7
9
  from typing import Any, Optional, Generic, TypeVar
8
- from pydantic import BaseModel
10
+ from pydantic import BaseModel, model_validator
9
11
 
10
12
  from .types import TypedMooseResource, T
11
13
  from .olap_table import OlapTable, OlapConfig
@@ -13,6 +15,7 @@ from .stream import Stream, StreamConfig, DeadLetterQueue
13
15
  from .ingest_api import IngestApi, IngestConfig, IngestConfigWithDestination
14
16
  from .life_cycle import LifeCycle
15
17
 
18
+
16
19
  class IngestPipelineConfig(BaseModel):
17
20
  """Configuration for creating a complete ingestion pipeline.
18
21
 
@@ -23,22 +26,47 @@ class IngestPipelineConfig(BaseModel):
23
26
  Attributes:
24
27
  table: Configuration for the OLAP table component.
25
28
  stream: Configuration for the stream component.
26
- ingest: Configuration for the ingest API component.
29
+ ingest_api: Configuration for the ingest API component.
27
30
  dead_letter_queue: Configuration for the dead letter queue.
28
31
  version: Optional version string applied to all created components.
29
32
  path: Optional custom path for the ingestion API endpoint.
30
33
  metadata: Optional metadata for the ingestion pipeline.
31
34
  life_cycle: Determines how changes in code will propagate to the resources.
32
35
  """
36
+
33
37
  table: bool | OlapConfig = True
34
38
  stream: bool | StreamConfig = True
35
- ingest: bool | IngestConfig = True
39
+ ingest_api: bool | IngestConfig = True
36
40
  dead_letter_queue: bool | StreamConfig = True
37
41
  version: Optional[str] = None
38
42
  path: Optional[str] = None
39
43
  metadata: Optional[dict] = None
40
44
  life_cycle: Optional[LifeCycle] = None
41
45
 
46
+ # Legacy support - will be removed in future version
47
+ ingest: Optional[bool | IngestConfig] = None
48
+
49
+ @model_validator(mode="before")
50
+ @classmethod
51
+ def handle_legacy_ingest_param(cls, data):
52
+ """Handle backwards compatibility for the deprecated 'ingest' parameter."""
53
+ if isinstance(data, dict) and "ingest" in data:
54
+ warnings.warn(
55
+ "The 'ingest' parameter is deprecated and will be removed in a future version. "
56
+ "Please use 'ingest_api' instead.",
57
+ DeprecationWarning,
58
+ stacklevel=3,
59
+ )
60
+ # Make a copy first to avoid mutating the original dictionary
61
+ data = data.copy()
62
+ # If ingest_api is not explicitly set, use the ingest value
63
+ if "ingest_api" not in data:
64
+ data["ingest_api"] = data["ingest"]
65
+ # Remove the legacy parameter
66
+ del data["ingest"]
67
+ return data
68
+
69
+
42
70
  class IngestPipeline(TypedMooseResource, Generic[T]):
43
71
  """Creates and configures a linked Table, Stream, and Ingest API pipeline.
44
72
 
@@ -60,6 +88,7 @@ class IngestPipeline(TypedMooseResource, Generic[T]):
60
88
  name (str): The base name of the pipeline.
61
89
  model_type (type[T]): The Pydantic model associated with this pipeline.
62
90
  """
91
+
63
92
  table: Optional[OlapTable[T]] = None
64
93
  stream: Optional[Stream[T]] = None
65
94
  ingest_api: Optional[IngestApi[T]] = None
@@ -126,23 +155,37 @@ class IngestPipeline(TypedMooseResource, Generic[T]):
126
155
  stream_metadata = config.metadata
127
156
  ingest_metadata = config.metadata
128
157
  if config.table:
129
- table_config = (config.table if isinstance(config.table, OlapConfig) else
130
- OlapConfig(life_cycle=config.life_cycle))
158
+ table_config = (
159
+ config.table
160
+ if isinstance(config.table, OlapConfig)
161
+ else OlapConfig(life_cycle=config.life_cycle)
162
+ )
131
163
  if config.version:
132
164
  table_config.version = config.version
133
165
  table_config.metadata = table_metadata
134
166
  self.table = OlapTable(name, table_config, t=self._t)
135
167
  if config.dead_letter_queue:
136
- dlq_stream_config = StreamConfig() if config.dead_letter_queue is True else config.dead_letter_queue
168
+ dlq_stream_config = (
169
+ StreamConfig()
170
+ if config.dead_letter_queue is True
171
+ else config.dead_letter_queue
172
+ )
137
173
  if config.version:
138
174
  dlq_stream_config.version = config.version
139
175
  dlq_stream_config.metadata = stream_metadata
140
- self.dead_letter_queue = DeadLetterQueue(f"{name}DeadLetterQueue", dlq_stream_config, t=self._t)
176
+ self.dead_letter_queue = DeadLetterQueue(
177
+ f"{name}DeadLetterQueue", dlq_stream_config, t=self._t
178
+ )
141
179
  if config.stream:
142
- stream_config = (config.stream if isinstance(config.stream, StreamConfig) else
143
- StreamConfig(life_cycle=config.life_cycle))
180
+ stream_config = (
181
+ config.stream
182
+ if isinstance(config.stream, StreamConfig)
183
+ else StreamConfig(life_cycle=config.life_cycle)
184
+ )
144
185
  if config.table and stream_config.destination is not None:
145
- raise ValueError("The destination of the stream should be the table created in the IngestPipeline")
186
+ raise ValueError(
187
+ "The destination of the stream should be the table created in the IngestPipeline"
188
+ )
146
189
  stream_config.destination = self.table
147
190
  if self.dead_letter_queue is not None:
148
191
  stream_config.default_dead_letter_queue = self.dead_letter_queue
@@ -150,11 +193,11 @@ class IngestPipeline(TypedMooseResource, Generic[T]):
150
193
  stream_config.version = config.version
151
194
  stream_config.metadata = stream_metadata
152
195
  self.stream = Stream(name, stream_config, t=self._t)
153
- if config.ingest:
196
+ if config.ingest_api:
154
197
  if self.stream is None:
155
198
  raise ValueError("Ingest API needs a stream to write to.")
156
199
  ingest_config_dict = (
157
- IngestConfig() if config.ingest is True else config.ingest
200
+ IngestConfig() if config.ingest_api is True else config.ingest_api
158
201
  ).model_dump()
159
202
  ingest_config_dict["destination"] = self.stream
160
203
  if config.version:
@@ -165,4 +208,4 @@ class IngestPipeline(TypedMooseResource, Generic[T]):
165
208
  ingest_config_dict["dead_letter_queue"] = self.dead_letter_queue
166
209
  ingest_config_dict["metadata"] = ingest_metadata
167
210
  ingest_config = IngestConfigWithDestination(**ingest_config_dict)
168
- self.ingest_api = IngestApi(name, ingest_config, t=self._t)
211
+ self.ingest_api = IngestApi(name, ingest_config, t=self._t)
@@ -4,8 +4,10 @@ Lifecycle management definitions for Moose Data Model v2 (dmv2).
4
4
  This module defines how Moose manages the lifecycle of database resources
5
5
  when your code changes.
6
6
  """
7
+
7
8
  from enum import Enum
8
9
 
10
+
9
11
  class LifeCycle(Enum):
10
12
  """Defines how Moose manages the lifecycle of database resources when your code changes.
11
13
 
@@ -29,4 +31,4 @@ class LifeCycle(Enum):
29
31
  """External management - no automatic changes.
30
32
  Moose will not modify the database resources. You are responsible for managing
31
33
  the schema and ensuring it matches your code definitions manually.
32
- """
34
+ """