moose-lib 0.6.57__py3-none-any.whl → 0.6.58__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.
Potentially problematic release.
This version of moose-lib might be problematic. Click here for more details.
- moose_lib/dmv2/_registry.py +3 -1
- moose_lib/dmv2/consumption.py +81 -4
- moose_lib/dmv2/ingest_api.py +5 -0
- moose_lib/dmv2/ingest_pipeline.py +5 -0
- moose_lib/dmv2/registry.py +18 -3
- moose_lib/internal.py +7 -0
- {moose_lib-0.6.57.dist-info → moose_lib-0.6.58.dist-info}/METADATA +1 -1
- {moose_lib-0.6.57.dist-info → moose_lib-0.6.58.dist-info}/RECORD +11 -11
- /moose_lib/{dmv2-serializer.py → dmv2_serializer.py} +0 -0
- {moose_lib-0.6.57.dist-info → moose_lib-0.6.58.dist-info}/WHEEL +0 -0
- {moose_lib-0.6.57.dist-info → moose_lib-0.6.58.dist-info}/top_level.txt +0 -0
moose_lib/dmv2/_registry.py
CHANGED
|
@@ -13,5 +13,7 @@ _ingest_apis: Dict[str, Any] = {}
|
|
|
13
13
|
_apis: Dict[str, Any] = {}
|
|
14
14
|
# Alias map for O(1) fallback of sole versioned APIs: base name -> handler
|
|
15
15
|
_api_name_aliases: Dict[str, Any] = {}
|
|
16
|
+
# Map from custom paths to API instances for path-based lookup
|
|
17
|
+
_api_path_map: Dict[str, Any] = {}
|
|
16
18
|
_sql_resources: Dict[str, Any] = {}
|
|
17
|
-
_workflows: Dict[str, Any] = {}
|
|
19
|
+
_workflows: Dict[str, Any] = {}
|
moose_lib/dmv2/consumption.py
CHANGED
|
@@ -12,7 +12,7 @@ from pydantic import BaseModel
|
|
|
12
12
|
from pydantic.json_schema import JsonSchemaValue
|
|
13
13
|
|
|
14
14
|
from .types import BaseTypedResource, T, U
|
|
15
|
-
from ._registry import _apis, _api_name_aliases
|
|
15
|
+
from ._registry import _apis, _api_name_aliases, _api_path_map
|
|
16
16
|
|
|
17
17
|
# Global base URL configuration
|
|
18
18
|
_global_base_url: Optional[str] = None
|
|
@@ -48,9 +48,11 @@ class ApiConfig(BaseModel):
|
|
|
48
48
|
|
|
49
49
|
Attributes:
|
|
50
50
|
version: Optional version string.
|
|
51
|
+
path: Optional custom path for the API endpoint. If not specified, defaults to the API name.
|
|
51
52
|
metadata: Optional metadata for the API.
|
|
52
53
|
"""
|
|
53
54
|
version: Optional[str] = None
|
|
55
|
+
path: Optional[str] = None
|
|
54
56
|
metadata: Optional[dict] = None
|
|
55
57
|
|
|
56
58
|
|
|
@@ -113,12 +115,68 @@ class Api(BaseTypedResource, Generic[U]):
|
|
|
113
115
|
else:
|
|
114
116
|
# Neither provided, use default config
|
|
115
117
|
self.config = ApiConfig()
|
|
116
|
-
|
|
118
|
+
|
|
117
119
|
self.query_function = query_function
|
|
118
120
|
self.metadata = getattr(self.config, 'metadata', {}) or {}
|
|
119
121
|
key = _generate_api_key(name, self.config.version)
|
|
122
|
+
|
|
123
|
+
# Check for duplicate registration
|
|
124
|
+
if key in _apis:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"Consumption API with name {name} and version {self.config.version} already exists"
|
|
127
|
+
)
|
|
120
128
|
_apis[key] = self
|
|
121
129
|
|
|
130
|
+
# Register by custom path if provided
|
|
131
|
+
if self.config.path:
|
|
132
|
+
# For unversioned APIs or when path should be used as-is
|
|
133
|
+
if self.config.version is None:
|
|
134
|
+
# Check for collision
|
|
135
|
+
if self.config.path in _api_path_map:
|
|
136
|
+
existing = _api_path_map[self.config.path]
|
|
137
|
+
raise ValueError(
|
|
138
|
+
f'Cannot register API "{name}" with custom path "{self.config.path}" - '
|
|
139
|
+
f'this path is already used by API "{existing.name}"'
|
|
140
|
+
)
|
|
141
|
+
# Only register unversioned path for unversioned APIs
|
|
142
|
+
_api_path_map[self.config.path] = self
|
|
143
|
+
else:
|
|
144
|
+
# For versioned APIs, check if version is already in the path
|
|
145
|
+
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)] == '/')
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if path_ends_with_version:
|
|
154
|
+
# Path already contains version, check for collision
|
|
155
|
+
if self.config.path in _api_path_map:
|
|
156
|
+
existing = _api_path_map[self.config.path]
|
|
157
|
+
raise ValueError(
|
|
158
|
+
f'Cannot register API "{name}" with path "{self.config.path}" - '
|
|
159
|
+
f'this path is already used by API "{existing.name}"'
|
|
160
|
+
)
|
|
161
|
+
_api_path_map[self.config.path] = self
|
|
162
|
+
else:
|
|
163
|
+
# Path doesn't contain version, register with version appended
|
|
164
|
+
path_with_version = f"{self.config.path.rstrip('/')}/{self.config.version}"
|
|
165
|
+
|
|
166
|
+
# Check for collision on versioned path
|
|
167
|
+
if path_with_version in _api_path_map:
|
|
168
|
+
existing = _api_path_map[path_with_version]
|
|
169
|
+
raise ValueError(
|
|
170
|
+
f'Cannot register API "{name}" with path "{path_with_version}" - '
|
|
171
|
+
f'this path is already used by API "{existing.name}"'
|
|
172
|
+
)
|
|
173
|
+
_api_path_map[path_with_version] = self
|
|
174
|
+
|
|
175
|
+
# Also register the unversioned path if not already claimed
|
|
176
|
+
# (This is intentionally more permissive - first API gets the unversioned path)
|
|
177
|
+
if self.config.path not in _api_path_map:
|
|
178
|
+
_api_path_map[self.config.path] = self
|
|
179
|
+
|
|
122
180
|
# Maintain alias for base name:
|
|
123
181
|
# - If explicit unversioned registered, alias -> that
|
|
124
182
|
# - Else, if exactly one versioned exists, alias -> that
|
|
@@ -205,8 +263,27 @@ class Api(BaseTypedResource, Generic[U]):
|
|
|
205
263
|
"MOOSE_BASE_URL environment variable, or pass base_url parameter."
|
|
206
264
|
)
|
|
207
265
|
|
|
208
|
-
# Construct the API endpoint URL
|
|
209
|
-
|
|
266
|
+
# Construct the API endpoint URL using custom path or default to name
|
|
267
|
+
if self.config.path:
|
|
268
|
+
# Check if the custom path already contains the version
|
|
269
|
+
if self.config.version:
|
|
270
|
+
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)] == '/')
|
|
276
|
+
)
|
|
277
|
+
if path_ends_with_version:
|
|
278
|
+
path = self.config.path
|
|
279
|
+
else:
|
|
280
|
+
path = f"{self.config.path.rstrip('/')}/{self.config.version}"
|
|
281
|
+
else:
|
|
282
|
+
path = self.config.path
|
|
283
|
+
else:
|
|
284
|
+
# Default to name with optional version
|
|
285
|
+
path = self.name if not self.config.version else f"{self.name}/{self.config.version}"
|
|
286
|
+
url = f"{effective_base_url.rstrip('/')}/api/{path}"
|
|
210
287
|
|
|
211
288
|
# Convert Pydantic model to dictionary
|
|
212
289
|
params_dict = params.model_dump()
|
moose_lib/dmv2/ingest_api.py
CHANGED
|
@@ -17,9 +17,11 @@ class IngestConfig(BaseModel):
|
|
|
17
17
|
|
|
18
18
|
Attributes:
|
|
19
19
|
version: Optional version string.
|
|
20
|
+
path: Optional custom path for the ingestion endpoint.
|
|
20
21
|
metadata: Optional metadata for the ingestion point.
|
|
21
22
|
"""
|
|
22
23
|
version: Optional[str] = None
|
|
24
|
+
path: Optional[str] = None
|
|
23
25
|
metadata: Optional[dict] = None
|
|
24
26
|
|
|
25
27
|
@dataclasses.dataclass
|
|
@@ -28,12 +30,15 @@ class IngestConfigWithDestination[T: BaseModel]:
|
|
|
28
30
|
|
|
29
31
|
Attributes:
|
|
30
32
|
destination: The `Stream` where ingested data will be sent.
|
|
33
|
+
dead_letter_queue: Optional dead letter queue for failed messages.
|
|
31
34
|
version: Optional version string.
|
|
35
|
+
path: Optional custom path for the ingestion endpoint.
|
|
32
36
|
metadata: Optional metadata for the ingestion configuration.
|
|
33
37
|
"""
|
|
34
38
|
destination: Stream[T]
|
|
35
39
|
dead_letter_queue: Optional[DeadLetterQueue[T]] = None
|
|
36
40
|
version: Optional[str] = None
|
|
41
|
+
path: Optional[str] = None
|
|
37
42
|
metadata: Optional[dict] = None
|
|
38
43
|
|
|
39
44
|
class IngestApi(TypedMooseResource, Generic[T]):
|
|
@@ -24,7 +24,9 @@ class IngestPipelineConfig(BaseModel):
|
|
|
24
24
|
table: Configuration for the OLAP table component.
|
|
25
25
|
stream: Configuration for the stream component.
|
|
26
26
|
ingest: Configuration for the ingest API component.
|
|
27
|
+
dead_letter_queue: Configuration for the dead letter queue.
|
|
27
28
|
version: Optional version string applied to all created components.
|
|
29
|
+
path: Optional custom path for the ingestion API endpoint.
|
|
28
30
|
metadata: Optional metadata for the ingestion pipeline.
|
|
29
31
|
life_cycle: Determines how changes in code will propagate to the resources.
|
|
30
32
|
"""
|
|
@@ -33,6 +35,7 @@ class IngestPipelineConfig(BaseModel):
|
|
|
33
35
|
ingest: bool | IngestConfig = True
|
|
34
36
|
dead_letter_queue: bool | StreamConfig = True
|
|
35
37
|
version: Optional[str] = None
|
|
38
|
+
path: Optional[str] = None
|
|
36
39
|
metadata: Optional[dict] = None
|
|
37
40
|
life_cycle: Optional[LifeCycle] = None
|
|
38
41
|
|
|
@@ -154,6 +157,8 @@ class IngestPipeline(TypedMooseResource, Generic[T]):
|
|
|
154
157
|
ingest_config_dict["destination"] = self.stream
|
|
155
158
|
if config.version:
|
|
156
159
|
ingest_config_dict["version"] = config.version
|
|
160
|
+
if config.path:
|
|
161
|
+
ingest_config_dict["path"] = config.path
|
|
157
162
|
if self.dead_letter_queue:
|
|
158
163
|
ingest_config_dict["dead_letter_queue"] = self.dead_letter_queue
|
|
159
164
|
ingest_config_dict["metadata"] = ingest_metadata
|
moose_lib/dmv2/registry.py
CHANGED
|
@@ -19,6 +19,7 @@ from ._registry import (
|
|
|
19
19
|
_sql_resources,
|
|
20
20
|
_workflows,
|
|
21
21
|
_api_name_aliases,
|
|
22
|
+
_api_path_map,
|
|
22
23
|
)
|
|
23
24
|
|
|
24
25
|
def get_tables() -> Dict[str, OlapTable]:
|
|
@@ -50,11 +51,25 @@ def get_apis() -> Dict[str, Api]:
|
|
|
50
51
|
return _apis
|
|
51
52
|
|
|
52
53
|
def get_api(name: str) -> Optional[Api]:
|
|
53
|
-
"""Get a registered API by name.
|
|
54
|
+
"""Get a registered API by name or path.
|
|
54
55
|
|
|
55
|
-
Supports
|
|
56
|
+
Supports:
|
|
57
|
+
- Direct lookup by name:version
|
|
58
|
+
- Unversioned lookup by name via alias map when only a single versioned API exists
|
|
59
|
+
- Lookup by custom path (if configured)
|
|
56
60
|
"""
|
|
57
|
-
|
|
61
|
+
# Try direct lookup first
|
|
62
|
+
api = _apis.get(name)
|
|
63
|
+
if api:
|
|
64
|
+
return api
|
|
65
|
+
|
|
66
|
+
# Try alias lookup
|
|
67
|
+
api = _api_name_aliases.get(name)
|
|
68
|
+
if api:
|
|
69
|
+
return api
|
|
70
|
+
|
|
71
|
+
# Try path-based lookup
|
|
72
|
+
return _api_path_map.get(name)
|
|
58
73
|
|
|
59
74
|
def get_sql_resources() -> Dict[str, SqlResource]:
|
|
60
75
|
"""Get all registered SQL resources."""
|
moose_lib/internal.py
CHANGED
|
@@ -172,7 +172,9 @@ class IngestApiConfig(BaseModel):
|
|
|
172
172
|
name: Name of the Ingest API.
|
|
173
173
|
columns: List of columns expected in the input data.
|
|
174
174
|
write_to: The target stream where the ingested data is written.
|
|
175
|
+
dead_letter_queue: Optional dead letter queue name.
|
|
175
176
|
version: Optional version string of the API configuration.
|
|
177
|
+
path: Optional custom path for the ingestion endpoint.
|
|
176
178
|
metadata: Optional metadata for the API.
|
|
177
179
|
"""
|
|
178
180
|
model_config = model_config
|
|
@@ -182,6 +184,7 @@ class IngestApiConfig(BaseModel):
|
|
|
182
184
|
write_to: Target
|
|
183
185
|
dead_letter_queue: Optional[str] = None
|
|
184
186
|
version: Optional[str] = None
|
|
187
|
+
path: Optional[str] = None
|
|
185
188
|
metadata: Optional[dict] = None
|
|
186
189
|
json_schema: dict[str, Any] = Field(serialization_alias="schema")
|
|
187
190
|
|
|
@@ -194,6 +197,7 @@ class InternalApiConfig(BaseModel):
|
|
|
194
197
|
query_params: List of columns representing the expected query parameters.
|
|
195
198
|
response_schema: JSON schema definition of the API's response body.
|
|
196
199
|
version: Optional version string of the API configuration.
|
|
200
|
+
path: Optional custom path for the API endpoint.
|
|
197
201
|
metadata: Optional metadata for the API.
|
|
198
202
|
"""
|
|
199
203
|
model_config = model_config
|
|
@@ -202,6 +206,7 @@ class InternalApiConfig(BaseModel):
|
|
|
202
206
|
query_params: List[Column]
|
|
203
207
|
response_schema: JsonSchemaValue
|
|
204
208
|
version: Optional[str] = None
|
|
209
|
+
path: Optional[str] = None
|
|
205
210
|
metadata: Optional[dict] = None
|
|
206
211
|
|
|
207
212
|
|
|
@@ -479,6 +484,7 @@ def to_infra_map() -> dict:
|
|
|
479
484
|
name=name,
|
|
480
485
|
columns=_to_columns(api._t),
|
|
481
486
|
version=api.config.version,
|
|
487
|
+
path=api.config.path,
|
|
482
488
|
write_to=Target(
|
|
483
489
|
kind="stream",
|
|
484
490
|
name=api.config.destination.name
|
|
@@ -496,6 +502,7 @@ def to_infra_map() -> dict:
|
|
|
496
502
|
query_params=_to_columns(api.model_type),
|
|
497
503
|
response_schema=api.get_response_schema(),
|
|
498
504
|
version=api.config.version,
|
|
505
|
+
path=api.config.path,
|
|
499
506
|
metadata=getattr(api, "metadata", None),
|
|
500
507
|
)
|
|
501
508
|
|
|
@@ -2,8 +2,8 @@ moose_lib/__init__.py,sha256=LiUdVqmtASPqbMF53dXVZzCdU1jtFVx62_tiSbW65a0,162
|
|
|
2
2
|
moose_lib/blocks.py,sha256=gC8dlV3HI4R8lO2wAxgLzn8WjKEMBjLiKyNAy4bssGE,11379
|
|
3
3
|
moose_lib/commons.py,sha256=FUpRv8D3-LeGcjhcqtDyiimz5izwpCq53h50ydxC_uA,3711
|
|
4
4
|
moose_lib/data_models.py,sha256=HT8ROf7HLS_BBlWvF-zGR7P-4gYURdMgfMlXa0O4i4s,10744
|
|
5
|
-
moose_lib/
|
|
6
|
-
moose_lib/internal.py,sha256=
|
|
5
|
+
moose_lib/dmv2_serializer.py,sha256=CL_Pvvg8tJOT8Qk6hywDNzY8MYGhMVdTOw8arZi3jng,49
|
|
6
|
+
moose_lib/internal.py,sha256=InJ3Mw5E3kZU8LSZLw7NTqQtsCLeNeOHpzc9ueO__FM,21142
|
|
7
7
|
moose_lib/main.py,sha256=dLcRE4jshP6ViDVLj--Y83QRsEp0dpwh7WilSEZ4ICk,18541
|
|
8
8
|
moose_lib/query_param.py,sha256=kxcR09BMIsEg4o2qetjKrVu1YFRaLfMEzwzyGsKUpvA,6474
|
|
9
9
|
moose_lib/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -12,14 +12,14 @@ moose_lib/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
|
|
|
12
12
|
moose_lib/config/config_file.py,sha256=NyjY6YFraBel7vBk18lLkpGaPR1viKMAEv4ZldyfLIA,2585
|
|
13
13
|
moose_lib/config/runtime.py,sha256=h4SXRn4Mlbeh02lKN7HX2Mdp3QzySnZOeSvm8G-B3ko,3857
|
|
14
14
|
moose_lib/dmv2/__init__.py,sha256=3DVAtNMZUoP94CMJBFhuXfYEQXDbQUNKSgg9XnKqae0,2768
|
|
15
|
-
moose_lib/dmv2/_registry.py,sha256=
|
|
16
|
-
moose_lib/dmv2/consumption.py,sha256=
|
|
17
|
-
moose_lib/dmv2/ingest_api.py,sha256=
|
|
18
|
-
moose_lib/dmv2/ingest_pipeline.py,sha256=
|
|
15
|
+
moose_lib/dmv2/_registry.py,sha256=gsLuWOvekgQRVvjVjPRHW2hN48LWyOWpLSpS2I-CWHc,708
|
|
16
|
+
moose_lib/dmv2/consumption.py,sha256=sWfGwgCBQIIrhFEbx1CULfoN3rFFoy8uCBUefu4ejiw,12928
|
|
17
|
+
moose_lib/dmv2/ingest_api.py,sha256=XhvHHgGPXp-BuRpAALth-FRhanwy-zJQ_83Cg_RLolM,2586
|
|
18
|
+
moose_lib/dmv2/ingest_pipeline.py,sha256=dECi4ZM5G6me2rJM_2xzATmTFsJGxhI-cyOs5CnbTFs,7284
|
|
19
19
|
moose_lib/dmv2/life_cycle.py,sha256=wl0k6yzwU1MJ_fO_UkN29buoY5G6ChYZvfwigP9fVfM,1254
|
|
20
20
|
moose_lib/dmv2/materialized_view.py,sha256=3JbNLC26p7xOQK0DpgkreWKx7k5V3x3bz9a7kjDqKuU,4876
|
|
21
21
|
moose_lib/dmv2/olap_table.py,sha256=fqGEphHubfR3oxVb6cXwmHVgx7gvb5QO7GCgDNnwAJI,33938
|
|
22
|
-
moose_lib/dmv2/registry.py,sha256=
|
|
22
|
+
moose_lib/dmv2/registry.py,sha256=SZOXhSC3kizhPHKQN6ZWzoMZHl90AUG3H_WKVev5zIU,2680
|
|
23
23
|
moose_lib/dmv2/sql_resource.py,sha256=kUZoGqxhZMHMthtBZGYJBxTFjXkspXiWLXhJRYXgGUM,1864
|
|
24
24
|
moose_lib/dmv2/stream.py,sha256=jiUWBsjFalLLP63mikOxyHRdieiDAlzf9lXfLye-Wjc,10761
|
|
25
25
|
moose_lib/dmv2/types.py,sha256=5FsB0HLHFkYB-8cjJ0rtRUjqahVA-ToLr2JXT1lFiss,3276
|
|
@@ -34,7 +34,7 @@ tests/conftest.py,sha256=ZVJNbnr4DwbcqkTmePW6U01zAzE6QD0kNAEZjPG1f4s,169
|
|
|
34
34
|
tests/test_moose.py,sha256=mBsx_OYWmL8ppDzL_7Bd7xR6qf_i3-pCIO3wm2iQNaA,2136
|
|
35
35
|
tests/test_redis_client.py,sha256=d9_MLYsJ4ecVil_jPB2gW3Q5aWnavxmmjZg2uYI3LVo,3256
|
|
36
36
|
tests/test_s3queue_config.py,sha256=F05cnD61S2wBKPabcpEJxf55-DJGF4nLqwBb6aFbprc,9741
|
|
37
|
-
moose_lib-0.6.
|
|
38
|
-
moose_lib-0.6.
|
|
39
|
-
moose_lib-0.6.
|
|
40
|
-
moose_lib-0.6.
|
|
37
|
+
moose_lib-0.6.58.dist-info/METADATA,sha256=isbeDqTmxePp9Ddt0qIi20malUdk_g-8BCW5V-iAoX4,730
|
|
38
|
+
moose_lib-0.6.58.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
39
|
+
moose_lib-0.6.58.dist-info/top_level.txt,sha256=XEns2-4aCmGp2XjJAeEH9TAUcGONLnSLy6ycT9FSJh8,16
|
|
40
|
+
moose_lib-0.6.58.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|