mainsequence 6.0.51__tar.gz → 6.0.52__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-6.0.51 → mainsequence-6.0.52}/PKG-INFO +1 -1
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +7 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/metatables/core.py +119 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/PKG-INFO +1 -1
- {mainsequence-6.0.51 → mainsequence-6.0.52}/pyproject.toml +1 -1
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_meta_tables_client_models.py +157 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/LICENSE +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/README.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/maintenance/project-maintenance/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/sdk_project_execution/SKILL.md +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/__main__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/bootstrap.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/api.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/cli.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/config.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/ui.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/base.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/client.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/models_user.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/observability.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/utils.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/defaults.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/logconf.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/alembic.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/env.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/provider.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/registry.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/project_context.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/project_skills.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/scaffold_skills.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/setup.cfg +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_auth_precedence.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_build_operations_hashing.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_canonical_api_route_contract.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_cli.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_cli_migrations.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_client.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_dependency_extras.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_environment_resource_context.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_environment_response_contracts.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_filter_normalization.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_instrumentation.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_local_ops.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_logconf.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_meta_table_migrations.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_notification_uid_contract.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_observability_client.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_project_branch_contract.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_project_runtime_context.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_project_skills.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_run_configuration.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_scaffold_skills.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_scheduler_client_model.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_schema_names.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_secret_client_model.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_ssh_utils.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_update_statistics.py +0 -0
- {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_update_uid_guards.py +0 -0
|
@@ -108,6 +108,13 @@ platform-managed, Alembic-managed reservations and are validated before HTTP.
|
|
|
108
108
|
- Do not create SDK reset/reconcile commands for stale reserved state. If stale
|
|
109
109
|
reserved state exists, fail clearly and require an explicit backend/admin
|
|
110
110
|
repair path.
|
|
111
|
+
- Ordinary `MetaTable.delete()` must not bypass Alembic schema-management
|
|
112
|
+
protection. For a deliberate organization-admin teardown/reset only, use
|
|
113
|
+
`MetaTable.delete_with_cascade(confirm_cascade_delete=True, ...)` and set
|
|
114
|
+
`override_schema_management_protection=True` when deleting Alembic-managed
|
|
115
|
+
tables. Decide explicitly whether referencing MetaTables and time-indexed Data
|
|
116
|
+
Nodes are also deleted. This permanently deletes catalog rows and physical
|
|
117
|
+
tables; never use it as migration reconciliation or an Alembic downgrade.
|
|
111
118
|
- Do not write direct backend migration request bodies in examples. Use the CLI
|
|
112
119
|
and SDK provider APIs.
|
|
113
120
|
- Do not call platform-managed model `.register()` in normal application code.
|
|
@@ -1030,6 +1030,67 @@ class ManagedMetaTableFinalizeResponse(BasePydanticModel):
|
|
|
1030
1030
|
model_config = ConfigDict(extra="allow")
|
|
1031
1031
|
|
|
1032
1032
|
|
|
1033
|
+
class MetaTableCascadeDeleteRequest(BasePydanticModel):
|
|
1034
|
+
confirm_cascade_delete: Literal[True] = Field(
|
|
1035
|
+
...,
|
|
1036
|
+
description="Explicit confirmation required for destructive cascade deletion.",
|
|
1037
|
+
)
|
|
1038
|
+
delete_referencing_meta_tables: bool = Field(
|
|
1039
|
+
default=True,
|
|
1040
|
+
description="Delete MetaTables that reference the selected MetaTable.",
|
|
1041
|
+
)
|
|
1042
|
+
delete_referencing_dynamic_tables: bool = Field(
|
|
1043
|
+
default=True,
|
|
1044
|
+
description="Delete time-indexed Data Nodes that reference the selected MetaTable.",
|
|
1045
|
+
)
|
|
1046
|
+
override_schema_management_protection: bool = Field(
|
|
1047
|
+
default=False,
|
|
1048
|
+
description=(
|
|
1049
|
+
"Explicitly allow deletion of Alembic-managed MetaTables. The backend "
|
|
1050
|
+
"still requires organization-admin permission."
|
|
1051
|
+
),
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
@field_validator("confirm_cascade_delete", mode="before")
|
|
1055
|
+
@classmethod
|
|
1056
|
+
def _require_explicit_confirmation(cls, value: Any) -> Any:
|
|
1057
|
+
if value is not True:
|
|
1058
|
+
raise ValueError("confirm_cascade_delete must be explicitly set to true.")
|
|
1059
|
+
return value
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
class MetaTableCascadeDeletedMetaTable(BasePydanticModel):
|
|
1063
|
+
meta_table_uid: str
|
|
1064
|
+
physical_schema: str | None = None
|
|
1065
|
+
physical_table_name: str | None = None
|
|
1066
|
+
management_mode: MetaTableManagementMode
|
|
1067
|
+
physical_table_drop_scheduled: bool
|
|
1068
|
+
physical_table_drop_cascade: bool
|
|
1069
|
+
schema_management_protection_overridden: bool
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
class MetaTableCascadeDeletedDynamicTable(BasePydanticModel):
|
|
1073
|
+
type: Literal["meta_table"]
|
|
1074
|
+
table_kind: Literal["time_indexed"]
|
|
1075
|
+
time_indexed: Literal[True]
|
|
1076
|
+
uid: str
|
|
1077
|
+
identifier: str | None = None
|
|
1078
|
+
namespace: str | None = None
|
|
1079
|
+
physical_schema: str | None = None
|
|
1080
|
+
physical_table_name: str | None = None
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
class MetaTableCascadeDeleteResponse(BasePydanticModel):
|
|
1084
|
+
ok: Literal[True]
|
|
1085
|
+
action: Literal["delete_with_cascade"]
|
|
1086
|
+
root_meta_table_uid: str
|
|
1087
|
+
deleted_meta_tables: list[MetaTableCascadeDeletedMetaTable]
|
|
1088
|
+
deleted_dynamic_tables: list[MetaTableCascadeDeletedDynamicTable]
|
|
1089
|
+
deleted_meta_table_count: int = Field(..., ge=0)
|
|
1090
|
+
deleted_dynamic_table_count: int = Field(..., ge=0)
|
|
1091
|
+
blocking_edges: list[dict[str, Any]]
|
|
1092
|
+
|
|
1093
|
+
|
|
1033
1094
|
class DataSource(BasePydanticModel, BaseObjectOrm):
|
|
1034
1095
|
model_config = ConfigDict(extra="allow")
|
|
1035
1096
|
|
|
@@ -1558,6 +1619,60 @@ class MetaTable(
|
|
|
1558
1619
|
override_protection=override_protection,
|
|
1559
1620
|
)
|
|
1560
1621
|
|
|
1622
|
+
@classmethod
|
|
1623
|
+
def delete_with_cascade_by_uid(
|
|
1624
|
+
cls,
|
|
1625
|
+
uid: str,
|
|
1626
|
+
*,
|
|
1627
|
+
confirm_cascade_delete: bool | None = None,
|
|
1628
|
+
delete_referencing_meta_tables: bool = True,
|
|
1629
|
+
delete_referencing_dynamic_tables: bool = True,
|
|
1630
|
+
override_schema_management_protection: bool = False,
|
|
1631
|
+
timeout: int | float | tuple[float, float] | None = None,
|
|
1632
|
+
) -> MetaTableCascadeDeleteResponse:
|
|
1633
|
+
if uid in (None, ""):
|
|
1634
|
+
raise ValueError(f"{cls.__name__} uid is required for cascade deletion.")
|
|
1635
|
+
request = MetaTableCascadeDeleteRequest(
|
|
1636
|
+
confirm_cascade_delete=confirm_cascade_delete,
|
|
1637
|
+
delete_referencing_meta_tables=delete_referencing_meta_tables,
|
|
1638
|
+
delete_referencing_dynamic_tables=delete_referencing_dynamic_tables,
|
|
1639
|
+
override_schema_management_protection=override_schema_management_protection,
|
|
1640
|
+
)
|
|
1641
|
+
operation = f"{cls.__name__}.delete_with_cascade"
|
|
1642
|
+
request_payload: dict[str, Any] = {"json": _payload_json(request)}
|
|
1643
|
+
query_context = cls._sdk_owned_query_context(operation)
|
|
1644
|
+
if query_context:
|
|
1645
|
+
request_payload["params"] = query_context
|
|
1646
|
+
response = make_request(
|
|
1647
|
+
s=cls.build_session(),
|
|
1648
|
+
loaders=cls.LOADERS,
|
|
1649
|
+
r_type="POST",
|
|
1650
|
+
url=(f"{cls.get_object_url().rstrip('/')}/{uid}/delete-with-cascade/"),
|
|
1651
|
+
payload=request_payload,
|
|
1652
|
+
time_out=timeout,
|
|
1653
|
+
)
|
|
1654
|
+
if response.status_code != 200:
|
|
1655
|
+
raise_for_response(response, payload=request_payload)
|
|
1656
|
+
return MetaTableCascadeDeleteResponse.model_validate(response.json())
|
|
1657
|
+
|
|
1658
|
+
def delete_with_cascade(
|
|
1659
|
+
self,
|
|
1660
|
+
*,
|
|
1661
|
+
confirm_cascade_delete: bool | None = None,
|
|
1662
|
+
delete_referencing_meta_tables: bool = True,
|
|
1663
|
+
delete_referencing_dynamic_tables: bool = True,
|
|
1664
|
+
override_schema_management_protection: bool = False,
|
|
1665
|
+
timeout: int | float | tuple[float, float] | None = None,
|
|
1666
|
+
) -> MetaTableCascadeDeleteResponse:
|
|
1667
|
+
return type(self).delete_with_cascade_by_uid(
|
|
1668
|
+
self._public_uid(),
|
|
1669
|
+
confirm_cascade_delete=confirm_cascade_delete,
|
|
1670
|
+
delete_referencing_meta_tables=delete_referencing_meta_tables,
|
|
1671
|
+
delete_referencing_dynamic_tables=delete_referencing_dynamic_tables,
|
|
1672
|
+
override_schema_management_protection=override_schema_management_protection,
|
|
1673
|
+
timeout=timeout,
|
|
1674
|
+
)
|
|
1675
|
+
|
|
1561
1676
|
@classmethod
|
|
1562
1677
|
def register(
|
|
1563
1678
|
cls,
|
|
@@ -4967,6 +5082,10 @@ __all__ = [
|
|
|
4967
5082
|
"ManagedMetaTableCollectionCreateRow",
|
|
4968
5083
|
"ManagedTimeIndexMetaTableCollectionCreateRow",
|
|
4969
5084
|
"MetaTable",
|
|
5085
|
+
"MetaTableCascadeDeleteRequest",
|
|
5086
|
+
"MetaTableCascadeDeleteResponse",
|
|
5087
|
+
"MetaTableCascadeDeletedDynamicTable",
|
|
5088
|
+
"MetaTableCascadeDeletedMetaTable",
|
|
4970
5089
|
"MetaTableMigrationConnection",
|
|
4971
5090
|
"MetaTableMigrationConnectionRequest",
|
|
4972
5091
|
"MetaTableColumnContract",
|
|
@@ -8,6 +8,7 @@ from pydantic import ValidationError
|
|
|
8
8
|
|
|
9
9
|
import mainsequence.client.metatables as meta_table_models
|
|
10
10
|
import mainsequence.project_context as project_context
|
|
11
|
+
from mainsequence.client.exceptions import ConflictError, PermissionDeniedError
|
|
11
12
|
from mainsequence.meta_tables.compiled_sql.v1 import build_operation, compile_sqlalchemy_statement
|
|
12
13
|
|
|
13
14
|
ENVIRONMENT_UID = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"
|
|
@@ -88,6 +89,162 @@ def _project_context():
|
|
|
88
89
|
)
|
|
89
90
|
|
|
90
91
|
|
|
92
|
+
def _cascade_delete_response():
|
|
93
|
+
return {
|
|
94
|
+
"ok": True,
|
|
95
|
+
"action": "delete_with_cascade",
|
|
96
|
+
"root_meta_table_uid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
97
|
+
"deleted_meta_tables": [
|
|
98
|
+
{
|
|
99
|
+
"meta_table_uid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
100
|
+
"physical_schema": "public",
|
|
101
|
+
"physical_table_name": "example_assets__asset",
|
|
102
|
+
"management_mode": "platform_managed",
|
|
103
|
+
"physical_table_drop_scheduled": True,
|
|
104
|
+
"physical_table_drop_cascade": True,
|
|
105
|
+
"schema_management_protection_overridden": True,
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
"deleted_dynamic_tables": [
|
|
109
|
+
{
|
|
110
|
+
"type": "meta_table",
|
|
111
|
+
"table_kind": "time_indexed",
|
|
112
|
+
"time_indexed": True,
|
|
113
|
+
"uid": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
|
|
114
|
+
"identifier": "AssetPrices",
|
|
115
|
+
"namespace": "example.assets",
|
|
116
|
+
"physical_schema": "public",
|
|
117
|
+
"physical_table_name": "example_assets__asset_prices",
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
"deleted_meta_table_count": 1,
|
|
121
|
+
"deleted_dynamic_table_count": 1,
|
|
122
|
+
"blocking_edges": [
|
|
123
|
+
{
|
|
124
|
+
"relationship_type": "meta_table_to_meta_table",
|
|
125
|
+
"source_table_kind": "time_indexed",
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_meta_table_confirmed_cascade_delete_posts_typed_contract(monkeypatch):
|
|
132
|
+
captured = {}
|
|
133
|
+
|
|
134
|
+
def fake_make_request(**kwargs):
|
|
135
|
+
captured.update(kwargs)
|
|
136
|
+
return _Response(_cascade_delete_response())
|
|
137
|
+
|
|
138
|
+
monkeypatch.setattr(meta_table_models, "make_request", fake_make_request)
|
|
139
|
+
monkeypatch.setattr(
|
|
140
|
+
meta_table_models.MetaTable,
|
|
141
|
+
"build_session",
|
|
142
|
+
classmethod(lambda cls: SimpleNamespace(headers={})),
|
|
143
|
+
)
|
|
144
|
+
meta_table = meta_table_models.MetaTable(
|
|
145
|
+
**_meta_table_response(
|
|
146
|
+
schema_management_mode="alembic_managed",
|
|
147
|
+
migration_provider_key="example:assets",
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
result = meta_table.delete_with_cascade(
|
|
152
|
+
confirm_cascade_delete=True,
|
|
153
|
+
delete_referencing_meta_tables=False,
|
|
154
|
+
delete_referencing_dynamic_tables=True,
|
|
155
|
+
override_schema_management_protection=True,
|
|
156
|
+
timeout=19,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
assert isinstance(result, meta_table_models.MetaTableCascadeDeleteResponse)
|
|
160
|
+
assert result.root_meta_table_uid == meta_table.uid
|
|
161
|
+
assert result.deleted_meta_table_count == 1
|
|
162
|
+
assert result.deleted_dynamic_tables[0].table_kind == "time_indexed"
|
|
163
|
+
assert captured["r_type"] == "POST"
|
|
164
|
+
assert captured["url"].endswith(f"/meta-tables/{meta_table.uid}/delete-with-cascade/")
|
|
165
|
+
assert captured["payload"] == {
|
|
166
|
+
"json": {
|
|
167
|
+
"confirm_cascade_delete": True,
|
|
168
|
+
"delete_referencing_meta_tables": False,
|
|
169
|
+
"delete_referencing_dynamic_tables": True,
|
|
170
|
+
"override_schema_management_protection": True,
|
|
171
|
+
},
|
|
172
|
+
"params": {"organization_environment_uid": ENVIRONMENT_UID},
|
|
173
|
+
}
|
|
174
|
+
assert captured["time_out"] == 19
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@pytest.mark.parametrize("confirmation", [None, False, 1, "true"])
|
|
178
|
+
def test_meta_table_cascade_delete_requires_explicit_true_confirmation(
|
|
179
|
+
monkeypatch,
|
|
180
|
+
confirmation,
|
|
181
|
+
):
|
|
182
|
+
monkeypatch.setattr(
|
|
183
|
+
meta_table_models,
|
|
184
|
+
"make_request",
|
|
185
|
+
lambda **kwargs: pytest.fail("missing confirmation must fail before HTTP"),
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
with pytest.raises(ValidationError, match="confirm_cascade_delete"):
|
|
189
|
+
meta_table_models.MetaTable.delete_with_cascade_by_uid(
|
|
190
|
+
"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
191
|
+
confirm_cascade_delete=confirmation,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@pytest.mark.parametrize(
|
|
196
|
+
("status_code", "error_type", "detail"),
|
|
197
|
+
[
|
|
198
|
+
(
|
|
199
|
+
403,
|
|
200
|
+
PermissionDeniedError,
|
|
201
|
+
"Only organization admins can perform this destructive MetaTable action.",
|
|
202
|
+
),
|
|
203
|
+
(
|
|
204
|
+
409,
|
|
205
|
+
ConflictError,
|
|
206
|
+
"Alembic-managed MetaTables require schema-management override.",
|
|
207
|
+
),
|
|
208
|
+
],
|
|
209
|
+
)
|
|
210
|
+
def test_meta_table_cascade_delete_preserves_typed_backend_errors(
|
|
211
|
+
monkeypatch,
|
|
212
|
+
status_code,
|
|
213
|
+
error_type,
|
|
214
|
+
detail,
|
|
215
|
+
):
|
|
216
|
+
captured = {}
|
|
217
|
+
|
|
218
|
+
def fake_make_request(**kwargs):
|
|
219
|
+
captured.update(kwargs)
|
|
220
|
+
return _Response(
|
|
221
|
+
{
|
|
222
|
+
"ok": False,
|
|
223
|
+
"code": "permission_denied"
|
|
224
|
+
if status_code == 403
|
|
225
|
+
else "alembic_managed_delete_blocked",
|
|
226
|
+
"detail": detail,
|
|
227
|
+
},
|
|
228
|
+
status_code=status_code,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
monkeypatch.setattr(meta_table_models, "make_request", fake_make_request)
|
|
232
|
+
|
|
233
|
+
with pytest.raises(error_type, match=detail) as error:
|
|
234
|
+
meta_table_models.MetaTable.delete_with_cascade_by_uid(
|
|
235
|
+
"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
236
|
+
confirm_cascade_delete=True,
|
|
237
|
+
override_schema_management_protection=True,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
assert error.value.status_code == status_code
|
|
241
|
+
assert error.value.payload["code"] in {
|
|
242
|
+
"permission_denied",
|
|
243
|
+
"alembic_managed_delete_blocked",
|
|
244
|
+
}
|
|
245
|
+
assert captured["payload"]["params"] == {"organization_environment_uid": ENVIRONMENT_UID}
|
|
246
|
+
|
|
247
|
+
|
|
91
248
|
@pytest.mark.parametrize(
|
|
92
249
|
"legacy_field",
|
|
93
250
|
["organization_environment_uid", "project_uid", "repository_branch"],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/data_access/exploration/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/sdk_project_execution/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/__init__.py
RENAMED
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/duckdb.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/sqlite.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/compiled_sql/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/build_operations.py
RENAMED
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/data_nodes.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/namespacing.py
RENAMED
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/persist_managers.py
RENAMED
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/run_operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/sqlalchemy_contracts.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_data_node_storage_dimension_queries.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|