mainsequence 4.1.18__tar.gz → 4.2.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.
- {mainsequence-4.1.18/mainsequence.egg-info → mainsequence-4.2.1}/PKG-INFO +2 -1
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +15 -22
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +44 -20
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/migrations.py +122 -71
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/metatables/core.py +31 -4
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/metatables/migrations.py +2 -5
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/__init__.py +8 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/build_operations.py +6 -5
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/persist_managers.py +9 -7
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/migrations.py +149 -61
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/sqlalchemy_contracts.py +111 -2
- {mainsequence-4.1.18 → mainsequence-4.2.1/mainsequence.egg-info}/PKG-INFO +2 -1
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence.egg-info/requires.txt +1 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/pyproject.toml +2 -1
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_build_operations_hashing.py +6 -23
- mainsequence-4.2.1/tests/test_cli_migrations.py +644 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_filter_normalization.py +2 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_meta_table_migrations.py +256 -26
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_meta_tables_client_models.py +56 -2
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_meta_tables_sqlalchemy_contracts.py +97 -41
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_run_configuration.py +27 -0
- mainsequence-4.1.18/tests/test_cli_migrations.py +0 -338
- {mainsequence-4.1.18 → mainsequence-4.2.1}/LICENSE +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/README.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/__main__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/api.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/cli.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/base.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/client.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/fastapi/auth.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/models_user.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/defaults.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/logconf.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/setup.cfg +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_cli.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_client.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_logconf.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.1.18 → mainsequence-4.2.1}/tests/test_workspace_snapshot.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mainsequence
|
|
3
|
-
Version: 4.1
|
|
3
|
+
Version: 4.2.1
|
|
4
4
|
Summary: Main Sequence SDK
|
|
5
5
|
Author-email: Main Sequence GmbH <dev@main-sequence.io>
|
|
6
6
|
License: MainSequence GmbH SDK License Agreement
|
|
@@ -63,6 +63,7 @@ Requires-Dist: opentelemetry-exporter-otlp
|
|
|
63
63
|
Requires-Dist: opentelemetry-sdk
|
|
64
64
|
Requires-Dist: pandas
|
|
65
65
|
Requires-Dist: psutil
|
|
66
|
+
Requires-Dist: psycopg2>=2.9.12
|
|
66
67
|
Requires-Dist: pydantic
|
|
67
68
|
Requires-Dist: pytz
|
|
68
69
|
Requires-Dist: pyyaml
|
{mainsequence-4.1.18 → mainsequence-4.2.1}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md
RENAMED
|
@@ -182,20 +182,14 @@ class PricesTable(PlatformTimeIndexMetaData, Base):
|
|
|
182
182
|
)
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
Storage registration is
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
Storage registration is migration-first. Add the storage model to the
|
|
186
|
+
MetaTable migration provider and run `mainsequence migrations upgrade --provider
|
|
187
|
+
... --to head`. Do not call `PricesTable.register()` directly and do not rely on
|
|
188
|
+
DataNode construction to register storage tables.
|
|
189
189
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
`PlatformTimeIndexMetaData.register()` is the storage lifecycle path.
|
|
195
|
-
Treat them as idempotent get-or-create operations: the platform returns the
|
|
196
|
-
registered metadata and UID, and the SDK records that metadata on the class. Do
|
|
197
|
-
not manually attach an existing UID, reconstruct a generic `MetaTable`, or use
|
|
198
|
-
manual bind helpers as an authoring step.
|
|
190
|
+
`PlatformTimeIndexMetaData.register()` remains SDK plumbing for the migration
|
|
191
|
+
workflow. Do not manually attach an existing UID, reconstruct a generic
|
|
192
|
+
`MetaTable`, or use manual bind helpers as an authoring step.
|
|
199
193
|
|
|
200
194
|
### 2. Keep DataNode As Update Logic
|
|
201
195
|
|
|
@@ -206,17 +200,16 @@ The DataNode constructor should accept:
|
|
|
206
200
|
- optional `hash_namespace`
|
|
207
201
|
|
|
208
202
|
The constructor `storage_table` is the output storage contract. Keep it out of
|
|
209
|
-
`DataNodeConfiguration`.
|
|
210
|
-
|
|
211
|
-
lifecycle automatically when the class is not yet bound.
|
|
203
|
+
`DataNodeConfiguration`. The storage class must already be registered by the
|
|
204
|
+
migration workflow before the node is constructed or run.
|
|
212
205
|
|
|
213
206
|
If the DataNode needs to select another DataNode's storage table as a
|
|
214
207
|
dependency, put that dependency storage reference in the config as
|
|
215
208
|
`type[PlatformTimeIndexMetaData]`. Do not add an extra constructor argument for
|
|
216
209
|
dependency storage tables. Config values of this type are hashed by the bound
|
|
217
210
|
`TimeIndexMetaData.uid` from `StorageClass.__time_index_metadata__`. If the
|
|
218
|
-
class is not yet bound, the
|
|
219
|
-
|
|
211
|
+
class is not yet bound, config serialization must fail and tell the user to run
|
|
212
|
+
the migration workflow.
|
|
220
213
|
|
|
221
214
|
Do not accept `test_node`. It has been removed. Use explicit
|
|
222
215
|
`hash_namespace(...)` or `hash_namespace="..."`.
|
|
@@ -398,10 +391,10 @@ Do not ask users to name these foreign keys. `MetaTableForeignKey(...)` derives
|
|
|
398
391
|
a stable contract name when `name` is omitted; `name=...` is only for deliberate
|
|
399
392
|
overrides.
|
|
400
393
|
|
|
401
|
-
Registration of the storage class follows the MetaTable lifecycle
|
|
402
|
-
|
|
403
|
-
local process registry keyed by `storage_hash`, and writes the
|
|
404
|
-
`MetaTable.uid` into the FK contract.
|
|
394
|
+
Registration of the storage class follows the MetaTable migration lifecycle.
|
|
395
|
+
Migration tooling recursively resolves/registers unresolved FK target model
|
|
396
|
+
classes, uses the local process registry keyed by `storage_hash`, and writes the
|
|
397
|
+
target `MetaTable.uid` into the FK contract.
|
|
405
398
|
|
|
406
399
|
Do not add DataNode configuration fields just to mutate storage metadata.
|
|
407
400
|
|
|
@@ -161,15 +161,18 @@ class Account(PlatformManagedMetaTable, Base):
|
|
|
161
161
|
},
|
|
162
162
|
)
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
account_meta_table = Account.register()
|
|
166
164
|
```
|
|
167
165
|
|
|
168
|
-
Registration metadata belongs on the class. Do not
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
Registration metadata belongs on the class. Do not call `Account.register()`
|
|
167
|
+
directly for platform-managed models. Add platform-managed models to the
|
|
168
|
+
selected `AlembicMetaTableMigration.metatable_models` list and let
|
|
169
|
+
`mainsequence migrations upgrade --provider ... --to head` resolve/register and
|
|
170
|
+
bind them.
|
|
171
171
|
|
|
172
|
-
For platform-managed registration, the data source is resolved from
|
|
172
|
+
For platform-managed migration registration, the data source is resolved from
|
|
173
|
+
the active Main Sequence project/session, the same way DataNode does. Do not
|
|
174
|
+
require or thread a `data_source_uid` through normal platform-managed example
|
|
175
|
+
code.
|
|
173
176
|
|
|
174
177
|
Only call `build_registration_request()` when the task explicitly needs to inspect or validate the payload before registration.
|
|
175
178
|
|
|
@@ -183,7 +186,8 @@ Do not add an environment variable for namespace in examples.
|
|
|
183
186
|
|
|
184
187
|
Do not add generic labels such as `"meta-table"` or `"platform-managed"` to examples. Keep labels specific to the example or domain.
|
|
185
188
|
|
|
186
|
-
Do not add a `MAINSEQUENCE_META_TABLE_REGISTER` toggle in
|
|
189
|
+
Do not add a `MAINSEQUENCE_META_TABLE_REGISTER` toggle in platform-managed
|
|
190
|
+
examples. Platform-managed examples should be migration-first.
|
|
187
191
|
|
|
188
192
|
### 3. Register parent tables before child tables
|
|
189
193
|
|
|
@@ -192,8 +196,8 @@ Foreign-key contracts reference the target `MetaTable` UID.
|
|
|
192
196
|
For `PlatformManagedMetaTable`, define foreign keys with
|
|
193
197
|
`MetaTableForeignKey(TargetModel, column=...)`. Do not write raw SQLAlchemy
|
|
194
198
|
table fullnames, `Parent.__table__.c.<column>` targets, or explicit target UID
|
|
195
|
-
maps in the platform-managed path.
|
|
196
|
-
|
|
199
|
+
maps in the platform-managed path. Migration is the lifecycle path. Migration
|
|
200
|
+
tooling resolves/registers unresolved target model classes, stores each
|
|
197
201
|
returned `MetaTable` in a local process registry keyed by `storage_hash`, and
|
|
198
202
|
uses the target `MetaTable.uid` in the child FK contract.
|
|
199
203
|
|
|
@@ -215,16 +219,19 @@ account_uid: Mapped[uuid.UUID] = mapped_column(
|
|
|
215
219
|
Every participating table must include `__metatable_description__` describing
|
|
216
220
|
both the schema and the table's intention.
|
|
217
221
|
|
|
218
|
-
|
|
222
|
+
Provider scope:
|
|
219
223
|
|
|
220
224
|
```python
|
|
221
|
-
|
|
225
|
+
migration = AlembicMetaTableMigration(
|
|
226
|
+
...,
|
|
227
|
+
metatable_models=[Account, Asset],
|
|
228
|
+
)
|
|
222
229
|
```
|
|
223
230
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
registration attempts for the same `storage_hash` and raises
|
|
227
|
-
recursive registration cycles.
|
|
231
|
+
Migration tooling registers `Account` first if `Asset` depends on it and it has
|
|
232
|
+
not already been bound in the current process. The local registry prevents
|
|
233
|
+
duplicate backend registration attempts for the same `storage_hash` and raises
|
|
234
|
+
a clear error for recursive registration cycles.
|
|
228
235
|
|
|
229
236
|
For `external_registered`, there is no platform-managed parent lookup. Register
|
|
230
237
|
the parent first, then build the child registration request with
|
|
@@ -260,27 +267,43 @@ For contract evolution, define or update one selected
|
|
|
260
267
|
- set `package`, `migration_namespace`, `script_location`, and `target_metadata`
|
|
261
268
|
- set `alembic_registry` to an `AlembicVersionMetaTable` subclass
|
|
262
269
|
- list the post-apply catalog scope in `metatable_models`
|
|
263
|
-
- generate, render, dry-run, apply, and
|
|
270
|
+
- generate, render, dry-run, apply, and refresh catalog bindings
|
|
264
271
|
through `mainsequence migrations ...` commands
|
|
265
272
|
|
|
266
273
|
`alembic_version_meta_table_uid` is the UID of the catalog binding for Alembic's
|
|
267
274
|
version table. It is not the UID of the table being migrated.
|
|
268
275
|
|
|
276
|
+
Application MetaTable catalog sync resolves existing rows by exact
|
|
277
|
+
`identifier`. If a model declares `__metatable_identifier__`, that value is the
|
|
278
|
+
global identity. If it does not, the SDK derives the identifier from
|
|
279
|
+
`[project].name` in `pyproject.toml` plus
|
|
280
|
+
`<model.__module__>.<model.__qualname__>`. Pin an explicit identifier when a
|
|
281
|
+
class is renamed or moved but must keep the same platform identity.
|
|
282
|
+
|
|
269
283
|
Do not ask users to construct backend migration payloads, call low-level
|
|
270
284
|
migration request models, or use SDK helper functions directly. The backend
|
|
271
285
|
request shape is reference material in the tutorial; the user-facing path is:
|
|
272
286
|
|
|
273
287
|
```bash
|
|
274
|
-
mainsequence migrations
|
|
275
|
-
mainsequence migrations revision --provider mainsequence_migrations:migration
|
|
288
|
+
mainsequence migrations current --provider mainsequence_migrations:migration
|
|
289
|
+
mainsequence migrations revision --provider mainsequence_migrations:migration
|
|
276
290
|
mainsequence migrations render --provider mainsequence_migrations:migration --to head
|
|
277
291
|
mainsequence migrations upgrade --provider mainsequence_migrations:migration --to head --dry-run
|
|
278
|
-
mainsequence migrations upgrade --provider mainsequence_migrations:migration --to head
|
|
292
|
+
mainsequence migrations upgrade --provider mainsequence_migrations:migration --to head
|
|
279
293
|
```
|
|
280
294
|
|
|
295
|
+
`current` and `upgrade` automatically register the provider's
|
|
296
|
+
`AlembicVersionMetaTable` binding when backend migration state is needed.
|
|
297
|
+
`revision` accepts optional `-m/--message`; if omitted, the CLI uses
|
|
298
|
+
`migration`. `revision --autogenerate` is optional and requires an explicit
|
|
299
|
+
`--sqlalchemy-url` for the baseline database.
|
|
300
|
+
|
|
281
301
|
The SQL must be Alembic-rendered from the selected provider. After SQL apply
|
|
282
302
|
succeeds, register or refresh only the application MetaTable catalog bindings
|
|
283
|
-
listed in `migration.metatable_models`.
|
|
303
|
+
listed in `migration.metatable_models`. Do not pass the Alembic version-table
|
|
304
|
+
data source into those application model registrations; each model uses its
|
|
305
|
+
own normal MetaTable data-source binding. A migration is not complete until
|
|
306
|
+
both backend SQL execution and catalog sync succeed.
|
|
284
307
|
|
|
285
308
|
Do not use SDK-managed migration artifact tables, artifact sync helpers, or custom
|
|
286
309
|
`operations()` migration modules.
|
|
@@ -336,6 +359,7 @@ Do not claim success until you have checked:
|
|
|
336
359
|
- migrations are scoped by an `AlembicMetaTableMigration` provider
|
|
337
360
|
- the provider's Alembic version-table binding is registered before apply/current
|
|
338
361
|
- post-apply catalog registration is scoped to `migration.metatable_models`
|
|
362
|
+
- catalog sync resolves application MetaTables by exact `identifier`
|
|
339
363
|
- user-facing migration instructions stay on the documented CLI/provider lifecycle
|
|
340
364
|
|
|
341
365
|
For related tables, also check:
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
4
|
import json
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Callable, Mapping
|
|
5
7
|
from typing import Any
|
|
6
8
|
|
|
7
9
|
import click
|
|
@@ -66,26 +68,67 @@ def _registry_uid(migration: AlembicMetaTableMigration) -> str:
|
|
|
66
68
|
uid = migration.alembic_registry.get_meta_table_uid()
|
|
67
69
|
if uid in (None, ""):
|
|
68
70
|
raise typer.BadParameter(
|
|
69
|
-
"Alembic registry MetaTable UID is not bound. Run
|
|
70
|
-
"or set __metatable_uid__ on the registry class.",
|
|
71
|
+
"Alembic registry MetaTable UID is not bound. Run current or upgrade "
|
|
72
|
+
"to auto-register the registry, or set __metatable_uid__ on the registry class.",
|
|
71
73
|
param_hint="--provider",
|
|
72
74
|
)
|
|
73
75
|
return str(uid)
|
|
74
76
|
|
|
75
77
|
|
|
78
|
+
def _model_reference(model: type[Any]) -> str:
|
|
79
|
+
module = getattr(model, "__module__", None)
|
|
80
|
+
qualname = getattr(model, "__qualname__", None)
|
|
81
|
+
if module and qualname:
|
|
82
|
+
return f"{module}.{qualname}"
|
|
83
|
+
return repr(model)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _meta_table_value(meta_table: Any, *names: str) -> Any:
|
|
87
|
+
if isinstance(meta_table, Mapping):
|
|
88
|
+
for name in names:
|
|
89
|
+
value = meta_table.get(name)
|
|
90
|
+
if value not in (None, ""):
|
|
91
|
+
return value
|
|
92
|
+
return None
|
|
93
|
+
for name in names:
|
|
94
|
+
value = getattr(meta_table, name, None)
|
|
95
|
+
if value not in (None, ""):
|
|
96
|
+
return value
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _print_metatable_resolution_callback(
|
|
101
|
+
migration: AlembicMetaTableMigration,
|
|
102
|
+
) -> Callable[[type[Any], str, str, Any | None], None]:
|
|
103
|
+
def _print(model: type[Any], identifier: str, status: str, meta_table: Any | None) -> None:
|
|
104
|
+
fields = [
|
|
105
|
+
f"identifier={identifier}",
|
|
106
|
+
f"model={_model_reference(model)}",
|
|
107
|
+
f"package={migration.package}",
|
|
108
|
+
f"migration_namespace={migration.migration_namespace}",
|
|
109
|
+
]
|
|
110
|
+
uid = _meta_table_value(meta_table, "uid", "meta_table_uid")
|
|
111
|
+
if uid is not None:
|
|
112
|
+
fields.append(f"uid={uid}")
|
|
113
|
+
physical_table_name = _meta_table_value(meta_table, "physical_table_name")
|
|
114
|
+
if physical_table_name is not None:
|
|
115
|
+
fields.append(f"physical_table_name={physical_table_name}")
|
|
116
|
+
typer.echo(f"migration MetaTable {status}: " + " ".join(fields), err=True)
|
|
117
|
+
|
|
118
|
+
return _print
|
|
119
|
+
|
|
120
|
+
|
|
76
121
|
def _ensure_registry(
|
|
77
122
|
migration: AlembicMetaTableMigration,
|
|
78
123
|
*,
|
|
79
|
-
data_source_uid: str | None,
|
|
80
124
|
timeout: float | None,
|
|
81
125
|
) -> None:
|
|
82
|
-
migration.ensure_alembic_registry(
|
|
126
|
+
migration.ensure_alembic_registry(timeout=timeout)
|
|
83
127
|
|
|
84
128
|
|
|
85
129
|
def _status_request(migration: AlembicMetaTableMigration) -> AlembicMigrationStatusRequest:
|
|
86
130
|
return AlembicMigrationStatusRequest(
|
|
87
131
|
alembic_version_meta_table_uid=_registry_uid(migration),
|
|
88
|
-
data_source_uid=migration.resolve_data_source_uid(),
|
|
89
132
|
package=migration.package,
|
|
90
133
|
migration_namespace=migration.migration_namespace,
|
|
91
134
|
)
|
|
@@ -117,7 +160,6 @@ def _operation(
|
|
|
117
160
|
) -> AlembicMigrationOperation:
|
|
118
161
|
return AlembicMigrationOperation(
|
|
119
162
|
alembic_version_meta_table_uid=_registry_uid(migration),
|
|
120
|
-
data_source_uid=migration.resolve_data_source_uid(),
|
|
121
163
|
package=migration.package,
|
|
122
164
|
migration_namespace=migration.migration_namespace,
|
|
123
165
|
revision=str(artifact.manifest["revision"]),
|
|
@@ -152,39 +194,35 @@ def _alembic_config(
|
|
|
152
194
|
return config
|
|
153
195
|
|
|
154
196
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"
|
|
160
|
-
help="Migration provider reference, for example msm.migrations:migration.",
|
|
161
|
-
),
|
|
162
|
-
data_source_uid: str | None = typer.Option(
|
|
163
|
-
None,
|
|
164
|
-
"--data-source-uid",
|
|
165
|
-
help="Explicit override for cross-data-source workflows.",
|
|
166
|
-
),
|
|
167
|
-
timeout: float | None = typer.Option(None, "--timeout"),
|
|
168
|
-
json_output: bool = typer.Option(False, "--json", help="Emit JSON."),
|
|
169
|
-
) -> None:
|
|
170
|
-
"""Register the provider's Alembic version table as an external MetaTable."""
|
|
197
|
+
def _next_sequential_revision_id(migration: AlembicMetaTableMigration) -> str:
|
|
198
|
+
try:
|
|
199
|
+
from alembic.script import ScriptDirectory
|
|
200
|
+
except ImportError as exc:
|
|
201
|
+
raise RuntimeError("Alembic is required for revision generation.") from exc
|
|
171
202
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
data_source_uid=data_source_uid,
|
|
175
|
-
timeout=timeout,
|
|
176
|
-
)
|
|
177
|
-
_emit(
|
|
178
|
-
{
|
|
179
|
-
"uid": migration.alembic_registry.get_meta_table_uid()
|
|
180
|
-
or getattr(meta_table, "uid", None),
|
|
181
|
-
"data_source_uid": migration.alembic_registry.get_data_source_uid(),
|
|
182
|
-
"alembic_version_table": migration.alembic_version_table,
|
|
183
|
-
"package": migration.package,
|
|
184
|
-
"migration_namespace": migration.migration_namespace,
|
|
185
|
-
},
|
|
186
|
-
json_output=json_output,
|
|
203
|
+
script = ScriptDirectory.from_config(
|
|
204
|
+
_alembic_config(migration, sqlalchemy_url="postgresql://")
|
|
187
205
|
)
|
|
206
|
+
heads = list(script.get_heads())
|
|
207
|
+
if len(heads) > 1:
|
|
208
|
+
raise typer.BadParameter(
|
|
209
|
+
"Sequential revision IDs require a single Alembic head. Pass --rev-id "
|
|
210
|
+
"explicitly for branched histories.",
|
|
211
|
+
param_hint="--rev-id",
|
|
212
|
+
)
|
|
213
|
+
if heads and not re.fullmatch(r"\d{4,}", str(heads[0])):
|
|
214
|
+
raise typer.BadParameter(
|
|
215
|
+
"Sequential revision IDs require the current Alembic head to be numeric. "
|
|
216
|
+
"Pass --rev-id explicitly for non-numeric histories.",
|
|
217
|
+
param_hint="--rev-id",
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
numeric_revisions: list[int] = []
|
|
221
|
+
for revision in script.walk_revisions():
|
|
222
|
+
revision_id = str(revision.revision)
|
|
223
|
+
if re.fullmatch(r"\d{4,}", revision_id):
|
|
224
|
+
numeric_revisions.append(int(revision_id))
|
|
225
|
+
return f"{max(numeric_revisions, default=0) + 1:04d}"
|
|
188
226
|
|
|
189
227
|
|
|
190
228
|
@migrations.command("current")
|
|
@@ -194,26 +232,30 @@ def current(
|
|
|
194
232
|
"--provider",
|
|
195
233
|
help="Migration provider reference, for example msm.migrations:migration.",
|
|
196
234
|
),
|
|
197
|
-
data_source_uid: str | None = typer.Option(
|
|
198
|
-
None,
|
|
199
|
-
"--data-source-uid",
|
|
200
|
-
help="Explicit override for cross-data-source workflows.",
|
|
201
|
-
),
|
|
202
235
|
timeout: float | None = typer.Option(None, "--timeout"),
|
|
203
236
|
json_output: bool = typer.Option(False, "--json", help="Emit JSON."),
|
|
204
237
|
) -> None:
|
|
205
238
|
"""Read current Alembic revision through the provider's registry MetaTable."""
|
|
206
239
|
|
|
207
240
|
migration = _load_migration(provider)
|
|
208
|
-
_ensure_registry(migration,
|
|
241
|
+
_ensure_registry(migration, timeout=timeout)
|
|
209
242
|
status = MetaTable.get_migration_status(_status_request(migration), timeout=timeout)
|
|
210
243
|
_emit(status, json_output=json_output)
|
|
211
244
|
|
|
212
245
|
|
|
213
246
|
@migrations.command("revision")
|
|
214
247
|
def revision(
|
|
215
|
-
message: str = typer.Option(
|
|
216
|
-
|
|
248
|
+
message: str | None = typer.Option(
|
|
249
|
+
None,
|
|
250
|
+
"--message",
|
|
251
|
+
"-m",
|
|
252
|
+
help="Alembic revision message. Defaults to 'migration'.",
|
|
253
|
+
),
|
|
254
|
+
autogenerate: bool = typer.Option(
|
|
255
|
+
False,
|
|
256
|
+
"--autogenerate",
|
|
257
|
+
help="Use Alembic autogenerate. Requires --sqlalchemy-url.",
|
|
258
|
+
),
|
|
217
259
|
provider: str | None = typer.Option(
|
|
218
260
|
None,
|
|
219
261
|
"--provider",
|
|
@@ -221,10 +263,10 @@ def revision(
|
|
|
221
263
|
),
|
|
222
264
|
rev_id: str | None = typer.Option(None, "--rev-id", help="Explicit Alembic revision id."),
|
|
223
265
|
head: str = typer.Option("head", "--head", help="Alembic head to base the revision on."),
|
|
224
|
-
sqlalchemy_url: str = typer.Option(
|
|
225
|
-
|
|
266
|
+
sqlalchemy_url: str | None = typer.Option(
|
|
267
|
+
None,
|
|
226
268
|
"--sqlalchemy-url",
|
|
227
|
-
help="SQLAlchemy URL passed to the Alembic environment.",
|
|
269
|
+
help="SQLAlchemy URL passed to the Alembic environment. Required for --autogenerate.",
|
|
228
270
|
),
|
|
229
271
|
json_output: bool = typer.Option(False, "--json", help="Emit JSON."),
|
|
230
272
|
) -> None:
|
|
@@ -236,11 +278,19 @@ def revision(
|
|
|
236
278
|
raise typer.BadParameter("Alembic is required for revision generation.") from exc
|
|
237
279
|
|
|
238
280
|
migration = _load_migration(provider)
|
|
281
|
+
resolved_message = (message or "").strip() or "migration"
|
|
282
|
+
if autogenerate and not sqlalchemy_url:
|
|
283
|
+
raise typer.BadParameter(
|
|
284
|
+
"--sqlalchemy-url is required with --autogenerate because Alembic "
|
|
285
|
+
"must connect to a baseline database to compute the diff.",
|
|
286
|
+
param_hint="--sqlalchemy-url",
|
|
287
|
+
)
|
|
288
|
+
resolved_rev_id = rev_id or _next_sequential_revision_id(migration)
|
|
239
289
|
script = command.revision(
|
|
240
|
-
_alembic_config(migration, sqlalchemy_url=sqlalchemy_url),
|
|
241
|
-
message=
|
|
290
|
+
_alembic_config(migration, sqlalchemy_url=sqlalchemy_url or "postgresql://"),
|
|
291
|
+
message=resolved_message,
|
|
242
292
|
autogenerate=autogenerate,
|
|
243
|
-
rev_id=
|
|
293
|
+
rev_id=resolved_rev_id,
|
|
244
294
|
head=head,
|
|
245
295
|
)
|
|
246
296
|
_emit(
|
|
@@ -284,6 +334,9 @@ def render(
|
|
|
284
334
|
migration = _load_migration(provider)
|
|
285
335
|
if direction not in {"upgrade", "downgrade"}:
|
|
286
336
|
raise typer.BadParameter("direction must be 'upgrade' or 'downgrade'.")
|
|
337
|
+
migration.resolve_or_register_metatable_models(
|
|
338
|
+
on_metatable_resolution=_print_metatable_resolution_callback(migration),
|
|
339
|
+
)
|
|
287
340
|
artifact = _render_artifact(
|
|
288
341
|
migration,
|
|
289
342
|
target_revision=target_revision,
|
|
@@ -305,18 +358,7 @@ def upgrade(
|
|
|
305
358
|
"--provider",
|
|
306
359
|
help="Migration provider reference, for example msm.migrations:migration.",
|
|
307
360
|
),
|
|
308
|
-
data_source_uid: str | None = typer.Option(
|
|
309
|
-
None,
|
|
310
|
-
"--data-source-uid",
|
|
311
|
-
help="Explicit override for cross-data-source workflows.",
|
|
312
|
-
),
|
|
313
|
-
apply: bool = typer.Option(False, "--apply", help="Apply after validating dry-run."),
|
|
314
361
|
dry_run: bool = typer.Option(False, "--dry-run", help="Validate without executing SQL."),
|
|
315
|
-
register_metatables: bool = typer.Option(
|
|
316
|
-
False,
|
|
317
|
-
"--register-metatables",
|
|
318
|
-
help="Register provider MetaTable models after a successful apply.",
|
|
319
|
-
),
|
|
320
362
|
sqlalchemy_url: str = typer.Option(
|
|
321
363
|
"postgresql://",
|
|
322
364
|
"--sqlalchemy-url",
|
|
@@ -327,11 +369,12 @@ def upgrade(
|
|
|
327
369
|
) -> None:
|
|
328
370
|
"""Dry-run or apply an Alembic-rendered SQL artifact through the backend."""
|
|
329
371
|
|
|
330
|
-
if apply and dry_run:
|
|
331
|
-
raise typer.BadParameter("Use either --apply or --dry-run, not both.")
|
|
332
|
-
|
|
333
372
|
migration = _load_migration(provider)
|
|
334
|
-
_ensure_registry(migration,
|
|
373
|
+
_ensure_registry(migration, timeout=timeout)
|
|
374
|
+
migration.resolve_or_register_metatable_models(
|
|
375
|
+
timeout=timeout,
|
|
376
|
+
on_metatable_resolution=_print_metatable_resolution_callback(migration),
|
|
377
|
+
)
|
|
335
378
|
status = MetaTable.get_migration_status(_status_request(migration), timeout=timeout)
|
|
336
379
|
current_revision = status.current_revision
|
|
337
380
|
artifact = _render_artifact(
|
|
@@ -349,7 +392,7 @@ def upgrade(
|
|
|
349
392
|
dry_run=True,
|
|
350
393
|
)
|
|
351
394
|
validation = MetaTable.apply_migration(validation_operation, timeout=timeout)
|
|
352
|
-
if
|
|
395
|
+
if dry_run:
|
|
353
396
|
_emit(validation, json_output=json_output)
|
|
354
397
|
return
|
|
355
398
|
if not validation.ok:
|
|
@@ -358,13 +401,21 @@ def upgrade(
|
|
|
358
401
|
|
|
359
402
|
apply_operation = validation_operation.model_copy(update={"dry_run": False})
|
|
360
403
|
result = MetaTable.apply_migration(apply_operation, timeout=timeout)
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
404
|
+
if not result.ok:
|
|
405
|
+
_emit(result, json_output=json_output)
|
|
406
|
+
raise typer.Exit(code=1)
|
|
407
|
+
applied_status = MetaTable.get_migration_status(_status_request(migration), timeout=timeout)
|
|
408
|
+
registered = migration.sync_metatable_catalog(timeout=timeout)
|
|
364
409
|
|
|
365
410
|
if json_output or _json_output_enabled():
|
|
366
411
|
_emit(
|
|
367
|
-
{
|
|
412
|
+
{
|
|
413
|
+
"validation": validation,
|
|
414
|
+
"apply": result,
|
|
415
|
+
"status": applied_status,
|
|
416
|
+
"registered": registered,
|
|
417
|
+
},
|
|
418
|
+
json_output=True,
|
|
368
419
|
)
|
|
369
420
|
return
|
|
370
421
|
_emit(result, json_output=False)
|
|
@@ -453,7 +453,13 @@ class MetaTableRegistrationRequest(BasePydanticModel):
|
|
|
453
453
|
management_mode: MetaTableManagementMode
|
|
454
454
|
storage_hash: str = Field(..., max_length=63, description="Canonical table storage hash.")
|
|
455
455
|
table_contract: MetaTableContract | dict[str, Any]
|
|
456
|
-
identifier: str | None =
|
|
456
|
+
identifier: str | None = Field(
|
|
457
|
+
default=None,
|
|
458
|
+
description=(
|
|
459
|
+
"Optional logical MetaTable identifier. Non-empty values are globally "
|
|
460
|
+
"unique per organization and are used to resolve migrated MetaTables."
|
|
461
|
+
),
|
|
462
|
+
)
|
|
457
463
|
namespace: str | None = None
|
|
458
464
|
description: str | None = None
|
|
459
465
|
protect_from_deletion: bool = False
|
|
@@ -869,7 +875,13 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
869
875
|
data_source: int | DynamicTableDataSource | dict[str, Any] | None = None
|
|
870
876
|
data_source_uid: str | None = None
|
|
871
877
|
storage_hash: str = Field(..., max_length=63, description="Canonical table storage hash.")
|
|
872
|
-
identifier: str | None =
|
|
878
|
+
identifier: str | None = Field(
|
|
879
|
+
default=None,
|
|
880
|
+
description=(
|
|
881
|
+
"Optional logical MetaTable identifier. Non-empty values are globally "
|
|
882
|
+
"unique per organization and are used to resolve migrated MetaTables."
|
|
883
|
+
),
|
|
884
|
+
)
|
|
873
885
|
namespace: str | None = None
|
|
874
886
|
description: str | None = None
|
|
875
887
|
labels: list[str] = Field(default_factory=list)
|
|
@@ -1387,7 +1399,13 @@ class TimeIndexMetaTableRegistrationRequest(BasePydanticModel):
|
|
|
1387
1399
|
max_length=63,
|
|
1388
1400
|
description="Canonical logical storage identity for the time-indexed MetaTable",
|
|
1389
1401
|
)
|
|
1390
|
-
identifier: str | None = Field(
|
|
1402
|
+
identifier: str | None = Field(
|
|
1403
|
+
None,
|
|
1404
|
+
description=(
|
|
1405
|
+
"Optional published storage identifier. Non-empty values are globally "
|
|
1406
|
+
"unique per organization."
|
|
1407
|
+
),
|
|
1408
|
+
)
|
|
1391
1409
|
namespace: str | None = Field(None, description="Optional published storage namespace")
|
|
1392
1410
|
description: str | None = Field(None, description="Optional storage description")
|
|
1393
1411
|
labels: list[str] = Field(default_factory=list)
|
|
@@ -1635,12 +1653,15 @@ class DataNodeUpdate(TableUpdateNode, BaseObjectOrm):
|
|
|
1635
1653
|
FILTERSET_FIELDS: ClassVar[dict[str, list[str]]] = {
|
|
1636
1654
|
"uid": ["in", "exact"],
|
|
1637
1655
|
"update_hash": ["exact"],
|
|
1656
|
+
"remote_table__uid": ["exact", "in"],
|
|
1638
1657
|
"remote_table__data_source__uid": ["exact", "in"],
|
|
1639
1658
|
"related_table__namespace": ["contains", "in", "isnull"],
|
|
1640
1659
|
}
|
|
1641
1660
|
FILTER_VALUE_NORMALIZERS: ClassVar[dict[str, str]] = {
|
|
1642
1661
|
"uid": "uid",
|
|
1643
1662
|
"uid__in": "uid",
|
|
1663
|
+
"remote_table__uid": "uid",
|
|
1664
|
+
"remote_table__uid__in": "uid",
|
|
1644
1665
|
"remote_table__data_source__uid": "uid",
|
|
1645
1666
|
"remote_table__data_source__uid__in": "uid",
|
|
1646
1667
|
}
|
|
@@ -2405,7 +2426,13 @@ class DataNodeUpdateDetails(BaseUpdateDetails, BasePydanticModel, BaseObjectOrm)
|
|
|
2405
2426
|
|
|
2406
2427
|
|
|
2407
2428
|
class TableMetaData(BaseModel):
|
|
2408
|
-
identifier: str =
|
|
2429
|
+
identifier: str | None = Field(
|
|
2430
|
+
default=None,
|
|
2431
|
+
description=(
|
|
2432
|
+
"Optional logical MetaTable identifier. Non-empty values are globally "
|
|
2433
|
+
"unique per organization."
|
|
2434
|
+
),
|
|
2435
|
+
)
|
|
2409
2436
|
description: str | None = None
|
|
2410
2437
|
|
|
2411
2438
|
|
|
@@ -25,7 +25,6 @@ class AlembicMigrationError(BasePydanticModel):
|
|
|
25
25
|
class AlembicMigrationOperation(BasePydanticModel):
|
|
26
26
|
version: AlembicMigrationVersion = ALEMBIC_MIGRATION_V1
|
|
27
27
|
alembic_version_meta_table_uid: str
|
|
28
|
-
data_source_uid: str
|
|
29
28
|
package: str = ""
|
|
30
29
|
migration_namespace: str = ""
|
|
31
30
|
revision: str
|
|
@@ -42,7 +41,6 @@ class AlembicMigrationOperation(BasePydanticModel):
|
|
|
42
41
|
|
|
43
42
|
class AlembicMigrationStatusRequest(BasePydanticModel):
|
|
44
43
|
alembic_version_meta_table_uid: str
|
|
45
|
-
data_source_uid: str
|
|
46
44
|
package: str = ""
|
|
47
45
|
migration_namespace: str = ""
|
|
48
46
|
|
|
@@ -56,7 +54,7 @@ class AlembicMigrationApplyResponse(BasePydanticModel):
|
|
|
56
54
|
dry_run: bool = False
|
|
57
55
|
alembic_version_meta_table_uid: str
|
|
58
56
|
alembic_version_table: str
|
|
59
|
-
data_source_uid: str
|
|
57
|
+
data_source_uid: str | None = None
|
|
60
58
|
package: str = ""
|
|
61
59
|
migration_namespace: str = ""
|
|
62
60
|
revision: str
|
|
@@ -76,7 +74,7 @@ class AlembicMigrationStatusResponse(BasePydanticModel):
|
|
|
76
74
|
version: AlembicMigrationVersion = ALEMBIC_MIGRATION_V1
|
|
77
75
|
alembic_version_meta_table_uid: str
|
|
78
76
|
alembic_version_table: str
|
|
79
|
-
data_source_uid: str
|
|
77
|
+
data_source_uid: str | None = None
|
|
80
78
|
package: str = ""
|
|
81
79
|
migration_namespace: str = ""
|
|
82
80
|
current_revision: str | None = None
|
|
@@ -127,7 +125,6 @@ def _get_migration_status(
|
|
|
127
125
|
"alembic_version_meta_table_uid",
|
|
128
126
|
payload.alembic_version_meta_table_uid,
|
|
129
127
|
)
|
|
130
|
-
response_payload.setdefault("data_source_uid", payload.data_source_uid)
|
|
131
128
|
response_payload.setdefault("package", payload.package)
|
|
132
129
|
response_payload.setdefault("migration_namespace", payload.migration_namespace)
|
|
133
130
|
return AlembicMigrationStatusResponse(**response_payload)
|
|
@@ -65,10 +65,18 @@ _LAZY_IMPORTS = {
|
|
|
65
65
|
".sqlalchemy_contracts",
|
|
66
66
|
"platform_managed_registration_request_from_sqlalchemy_model",
|
|
67
67
|
),
|
|
68
|
+
"platform_managed_migration_registration_context": (
|
|
69
|
+
".sqlalchemy_contracts",
|
|
70
|
+
"platform_managed_migration_registration_context",
|
|
71
|
+
),
|
|
68
72
|
"register_external_sqlalchemy_model": (
|
|
69
73
|
".sqlalchemy_contracts",
|
|
70
74
|
"register_external_sqlalchemy_model",
|
|
71
75
|
),
|
|
76
|
+
"resolve_metatable_identifier": (
|
|
77
|
+
".sqlalchemy_contracts",
|
|
78
|
+
"resolve_metatable_identifier",
|
|
79
|
+
),
|
|
72
80
|
"render_packaged_alembic_migration": (
|
|
73
81
|
".migrations",
|
|
74
82
|
"render_packaged_alembic_migration",
|