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.
Files changed (133) hide show
  1. {mainsequence-6.0.51 → mainsequence-6.0.52}/PKG-INFO +1 -1
  2. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +7 -0
  3. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/metatables/core.py +119 -0
  4. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/PKG-INFO +1 -1
  5. {mainsequence-6.0.51 → mainsequence-6.0.52}/pyproject.toml +1 -1
  6. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_meta_tables_client_models.py +157 -0
  7. {mainsequence-6.0.51 → mainsequence-6.0.52}/LICENSE +0 -0
  8. {mainsequence-6.0.51 → mainsequence-6.0.52}/README.md +0 -0
  9. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/AGENTS.md +0 -0
  10. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
  11. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  12. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  13. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
  14. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  15. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  16. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/maintenance/project-maintenance/SKILL.md +0 -0
  17. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  18. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  19. {mainsequence-6.0.51 → mainsequence-6.0.52}/agent_scaffold/skills/sdk_project_execution/SKILL.md +0 -0
  20. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/__init__.py +0 -0
  21. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/__main__.py +0 -0
  22. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/bootstrap.py +0 -0
  23. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/__init__.py +0 -0
  24. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/api.py +0 -0
  25. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/browser_auth.py +0 -0
  26. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/cli.py +0 -0
  27. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/config.py +0 -0
  28. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/docker_utils.py +0 -0
  29. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/doctor.py +0 -0
  30. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/local_ops.py +0 -0
  31. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/migrations.py +0 -0
  32. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/model_filters.py +0 -0
  33. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/project_status.py +0 -0
  34. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/pydantic_cli.py +0 -0
  35. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/sdk_utils.py +0 -0
  36. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/ssh_utils.py +0 -0
  37. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/cli/ui.py +0 -0
  38. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/__init__.py +0 -0
  39. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/agent_runtime_models.py +0 -0
  40. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/base.py +0 -0
  41. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/client.py +0 -0
  42. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/compute_validation.py +0 -0
  43. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  44. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  45. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
  46. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
  47. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/dtype_codec.py +0 -0
  48. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/exceptions.py +0 -0
  49. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/metatables/__init__.py +0 -0
  50. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/models_foundry.py +0 -0
  51. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/models_helpers.py +0 -0
  52. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/models_user.py +0 -0
  53. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/observability.py +0 -0
  54. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/client/utils.py +0 -0
  55. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/defaults.py +0 -0
  56. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/instrumentation/__init__.py +0 -0
  57. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/instrumentation/utils.py +0 -0
  58. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/logconf.py +0 -0
  59. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/__init__.py +0 -0
  60. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/__main__.py +0 -0
  61. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
  62. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
  63. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
  64. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
  65. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
  66. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/models.py +0 -0
  67. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
  68. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
  69. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  70. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  71. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/future_registry.py +0 -0
  72. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/hashing.py +0 -0
  73. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/__init__.py +0 -0
  74. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/alembic.py +0 -0
  75. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/env.py +0 -0
  76. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/provider.py +0 -0
  77. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/registry.py +0 -0
  78. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
  79. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
  80. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
  81. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
  82. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  83. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/schema_names.py +0 -0
  84. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
  85. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/project_context.py +0 -0
  86. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/project_skills.py +0 -0
  87. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/runtime_flags.py +0 -0
  88. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence/scaffold_skills.py +0 -0
  89. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/SOURCES.txt +0 -0
  90. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/dependency_links.txt +0 -0
  91. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/entry_points.txt +0 -0
  92. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/requires.txt +0 -0
  93. {mainsequence-6.0.51 → mainsequence-6.0.52}/mainsequence.egg-info/top_level.txt +0 -0
  94. {mainsequence-6.0.51 → mainsequence-6.0.52}/setup.cfg +0 -0
  95. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_auth_precedence.py +0 -0
  96. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_build_operations_hashing.py +0 -0
  97. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_canonical_api_route_contract.py +0 -0
  98. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_cli.py +0 -0
  99. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_cli_browser_auth.py +0 -0
  100. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_cli_migrations.py +0 -0
  101. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_client.py +0 -0
  102. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_data_access_mixin_dimension_audit.py +0 -0
  103. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_data_node_storage_dimension_queries.py +0 -0
  104. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_data_node_update_flow.py +0 -0
  105. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_dependency_extras.py +0 -0
  106. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_duckdb_interface_dimensions.py +0 -0
  107. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_environment_resource_context.py +0 -0
  108. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_environment_response_contracts.py +0 -0
  109. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_filter_normalization.py +0 -0
  110. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_instrumentation.py +0 -0
  111. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_local_ops.py +0 -0
  112. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_logconf.py +0 -0
  113. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_meta_table_migrations.py +0 -0
  114. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
  115. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_models_user_request_bound_auth.py +0 -0
  116. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_notification_uid_contract.py +0 -0
  117. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_observability_client.py +0 -0
  118. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_pod_project_resolution.py +0 -0
  119. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_project_batch_jobs_from_file.py +0 -0
  120. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_project_branch_contract.py +0 -0
  121. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_project_runtime_context.py +0 -0
  122. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_project_skills.py +0 -0
  123. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_run_configuration.py +0 -0
  124. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_scaffold_skills.py +0 -0
  125. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_scheduler_client_model.py +0 -0
  126. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_schema_names.py +0 -0
  127. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_secret_client_model.py +0 -0
  128. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_source_table_configuration.py +0 -0
  129. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_sqlite_interface_dimensions.py +0 -0
  130. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_ssh_utils.py +0 -0
  131. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_update_runner_uid_runtime.py +0 -0
  132. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_update_statistics.py +0 -0
  133. {mainsequence-6.0.51 → mainsequence-6.0.52}/tests/test_update_uid_guards.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 6.0.51
3
+ Version: 6.0.52
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: LicenseRef-MainSequence-Proprietary
@@ -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",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 6.0.51
3
+ Version: 6.0.52
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: LicenseRef-MainSequence-Proprietary
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "6.0.51"
7
+ version = "6.0.52"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -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