mainsequence 6.0.39__tar.gz → 6.0.41__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.39/mainsequence.egg-info → mainsequence-6.0.41}/PKG-INFO +1 -1
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/data_access/exploration/SKILL.md +6 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +2 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +4 -1
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +4 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/base.py +95 -4
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/metatables/core.py +76 -18
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/models_foundry.py +76 -8
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/models_helpers.py +9 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/project_context.py +19 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence.egg-info/SOURCES.txt +1 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/pyproject.toml +1 -1
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_cli.py +47 -9
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_cli_migrations.py +2 -1
- mainsequence-6.0.41/tests/test_environment_resource_context.py +168 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_filter_normalization.py +115 -27
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_meta_tables_client_models.py +58 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_project_runtime_context.py +35 -0
- mainsequence-6.0.41/tests/test_secret_client_model.py +18 -0
- mainsequence-6.0.39/tests/test_secret_client_model.py +0 -11
- {mainsequence-6.0.39 → mainsequence-6.0.41}/LICENSE +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/README.md +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/maintenance/project-maintenance/SKILL.md +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/sdk_project_execution/SKILL.md +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/__main__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/bootstrap.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/api.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/cli.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/config.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/cli/ui.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/client.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/models_user.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/client/utils.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/defaults.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/logconf.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/migrations/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/migrations/alembic.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/migrations/env.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/migrations/provider.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/migrations/registry.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/project_skills.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence/scaffold_skills.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/setup.cfg +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_auth_precedence.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_build_operations_hashing.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_canonical_api_route_contract.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_client.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_dependency_extras.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_instrumentation.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_local_ops.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_logconf.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_meta_table_migrations.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_notification_uid_contract.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_project_branch_contract.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_project_skills.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_run_configuration.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_scaffold_skills.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_schema_names.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_ssh_utils.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_update_statistics.py +0 -0
- {mainsequence-6.0.39 → mainsequence-6.0.41}/tests/test_update_uid_guards.py +0 -0
{mainsequence-6.0.39 → mainsequence-6.0.41}/agent_scaffold/skills/data_access/exploration/SKILL.md
RENAMED
|
@@ -154,6 +154,12 @@ field to filter by:
|
|
|
154
154
|
- `mainsequence data-node list --filter KEY=VALUE`
|
|
155
155
|
- `mainsequence data-node detail <DATA_NODE_STORAGE_UID>`
|
|
156
156
|
|
|
157
|
+
Collection listing is scoped to one Organization Environment. Inside a
|
|
158
|
+
registered project checkout, the CLI derives that scope from the process-frozen
|
|
159
|
+
Git-resolved ProjectBranch. Do not pass an Environment UID or branch UID. On an
|
|
160
|
+
unregistered branch, table operations fail when they require ProjectBranch
|
|
161
|
+
context while unrelated local development remains available.
|
|
162
|
+
|
|
157
163
|
Column lookup is also a different path. Use it only for schema-name discovery,
|
|
158
164
|
not as the default dataset discovery flow:
|
|
159
165
|
|
|
@@ -72,6 +72,8 @@ Before changing code, collect or infer:
|
|
|
72
72
|
- who owns the MetaTable catalog lifecycle
|
|
73
73
|
- who owns physical DDL: TS Manager, Alembic, or an external authority
|
|
74
74
|
- for `external_registered`, the target canonical `DataSource` UID
|
|
75
|
+
- the actual current Git branch; the SDK resolves its registered `ProjectBranch`
|
|
76
|
+
and Environment, and the user must not provide either UID
|
|
75
77
|
- for contract changes, the selected `AlembicMetaTableMigration` provider or provider module path
|
|
76
78
|
- for contract changes, the provider's `AlembicVersionMetaTable` root and whether it has been reserved
|
|
77
79
|
- for contract changes, the intended Alembic revision, parent/current revision, target revision, and updated SQLAlchemy declarations
|
|
@@ -158,7 +158,10 @@ Do not downgrade a secret into a constant for convenience.
|
|
|
158
158
|
|
|
159
159
|
### 6. `Constant` and `Secret` names are unique configuration identities
|
|
160
160
|
|
|
161
|
-
Treat `Constant.name` and `Secret.name` as unique
|
|
161
|
+
Treat `Constant.name` and `Secret.name` as unique Environment-level
|
|
162
|
+
configuration keys. Project-facing SDK and CLI operations derive the
|
|
163
|
+
Environment from the process-frozen current Git branch and registered
|
|
164
|
+
`ProjectBranch`. Never ask the user to provide an Environment UID or branch UID.
|
|
162
165
|
|
|
163
166
|
For creation or sync tasks:
|
|
164
167
|
|
|
@@ -195,6 +195,10 @@ push alone does not prove deployment success.
|
|
|
195
195
|
|
|
196
196
|
Use `Artifact` when the operational unit is a file.
|
|
197
197
|
|
|
198
|
+
Artifact and Bucket operations derive their Organization Environment from the
|
|
199
|
+
process-frozen current Git branch and registered `ProjectBranch`. Do not ask the
|
|
200
|
+
user to select an Environment UID or branch UID.
|
|
201
|
+
|
|
198
202
|
Examples:
|
|
199
203
|
|
|
200
204
|
- vendor drops
|
|
@@ -97,6 +97,26 @@ class CurrentProjectBranchCollectionMixin:
|
|
|
97
97
|
return list(cls.iter_filter_admin(timeout=timeout, **kwargs))
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
class CurrentProjectEnvironmentResourceMixin:
|
|
101
|
+
"""Scope project-facing Environment resources to the frozen Git context."""
|
|
102
|
+
|
|
103
|
+
SDK_OWNED_CONTEXT_FIELDS: ClassVar[frozenset[str]] = frozenset(
|
|
104
|
+
{"organization_project_environment_uid"}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
@classmethod
|
|
108
|
+
def _sdk_owned_query_context(cls, operation: str) -> dict[str, str]:
|
|
109
|
+
from mainsequence.project_context import resolve_project_environment_uid
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
"organization_project_environment_uid": resolve_project_environment_uid(operation)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def _sdk_owned_create_context(cls, operation: str) -> dict[str, str]:
|
|
117
|
+
return cls._sdk_owned_query_context(operation)
|
|
118
|
+
|
|
119
|
+
|
|
100
120
|
class BaseObjectOrm:
|
|
101
121
|
FILTERSET_FIELDS: ClassVar[dict[str, list[str]] | None] = None
|
|
102
122
|
FILTER_VALUE_NORMALIZERS: ClassVar[dict[str, str | Callable[..., Any]]] = {}
|
|
@@ -105,6 +125,7 @@ class BaseObjectOrm:
|
|
|
105
125
|
DESTROY_QUERY_PARAMS: ClassVar[dict[str, str | Callable[..., Any]] | None] = None
|
|
106
126
|
DESTROY_QUERY_PARAM_DESCRIPTIONS: ClassVar[dict[str, str] | None] = None
|
|
107
127
|
PUBLIC_LOOKUP_FIELD: ClassVar[str] = "uid"
|
|
128
|
+
SDK_OWNED_CONTEXT_FIELDS: ClassVar[frozenset[str]] = frozenset()
|
|
108
129
|
|
|
109
130
|
END_POINTS = {
|
|
110
131
|
"User": "users",
|
|
@@ -154,6 +175,37 @@ class BaseObjectOrm:
|
|
|
154
175
|
s = session
|
|
155
176
|
return s
|
|
156
177
|
|
|
178
|
+
@classmethod
|
|
179
|
+
def _sdk_owned_query_context(cls, operation: str) -> dict[str, Any]:
|
|
180
|
+
return {}
|
|
181
|
+
|
|
182
|
+
@classmethod
|
|
183
|
+
def _sdk_owned_create_context(cls, operation: str) -> dict[str, Any]:
|
|
184
|
+
return {}
|
|
185
|
+
|
|
186
|
+
@classmethod
|
|
187
|
+
def _reject_sdk_context_overrides(
|
|
188
|
+
cls,
|
|
189
|
+
values: dict[str, Any],
|
|
190
|
+
*,
|
|
191
|
+
operation: str,
|
|
192
|
+
) -> None:
|
|
193
|
+
overridden = sorted(set(values) & set(cls.SDK_OWNED_CONTEXT_FIELDS))
|
|
194
|
+
if overridden:
|
|
195
|
+
raise ValueError(
|
|
196
|
+
f"{operation} cannot override SDK-resolved context field(s): "
|
|
197
|
+
f"{', '.join(overridden)}."
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
def _with_sdk_owned_create_context(
|
|
202
|
+
cls,
|
|
203
|
+
operation: str,
|
|
204
|
+
values: dict[str, Any],
|
|
205
|
+
) -> dict[str, Any]:
|
|
206
|
+
cls._reject_sdk_context_overrides(values, operation=operation)
|
|
207
|
+
return {**values, **cls._sdk_owned_create_context(operation)}
|
|
208
|
+
|
|
157
209
|
@property
|
|
158
210
|
def s(self):
|
|
159
211
|
s = self.build_session()
|
|
@@ -468,11 +520,19 @@ class BaseObjectOrm:
|
|
|
468
520
|
"""
|
|
469
521
|
Generator variant: yields objects across all pages without accumulating into memory.
|
|
470
522
|
"""
|
|
523
|
+
operation = f"{cls.__name__}.filter"
|
|
524
|
+
cls._reject_sdk_context_overrides(kwargs, operation=operation)
|
|
471
525
|
filter_kwargs, read_query_kwargs = cls._split_filter_and_read_query_kwargs(kwargs)
|
|
472
526
|
normalized_filters = cls._normalize_filter_kwargs(filter_kwargs)
|
|
473
527
|
normalized_read_query = cls._normalize_read_query_kwargs(read_query_kwargs)
|
|
474
528
|
base_url = cls.get_object_url()
|
|
475
|
-
params = cls._parse_parameters_filter(
|
|
529
|
+
params = cls._parse_parameters_filter(
|
|
530
|
+
{
|
|
531
|
+
**normalized_filters,
|
|
532
|
+
**normalized_read_query,
|
|
533
|
+
**cls._sdk_owned_query_context(operation),
|
|
534
|
+
}
|
|
535
|
+
)
|
|
476
536
|
|
|
477
537
|
next_url = f"{base_url}/"
|
|
478
538
|
yielded = 0
|
|
@@ -534,6 +594,8 @@ class BaseObjectOrm:
|
|
|
534
594
|
Raises Exception if multiple or unexpected data is returned.
|
|
535
595
|
"""
|
|
536
596
|
if pk is not None:
|
|
597
|
+
operation = f"{cls.__name__}.get"
|
|
598
|
+
cls._reject_sdk_context_overrides(filters, operation=operation)
|
|
537
599
|
base_url = cls.get_object_url()
|
|
538
600
|
detail_url = f"{base_url}/{pk}/"
|
|
539
601
|
_, read_query_kwargs = cls._split_filter_and_read_query_kwargs(filters)
|
|
@@ -541,7 +603,11 @@ class BaseObjectOrm:
|
|
|
541
603
|
extra_params = {
|
|
542
604
|
key: value for key, value in filters.items() if key not in read_query_kwargs
|
|
543
605
|
}
|
|
544
|
-
params = {
|
|
606
|
+
params = {
|
|
607
|
+
**extra_params,
|
|
608
|
+
**normalized_read_query,
|
|
609
|
+
**cls._sdk_owned_query_context(operation),
|
|
610
|
+
}
|
|
545
611
|
|
|
546
612
|
r = make_request(
|
|
547
613
|
s=cls.build_session(),
|
|
@@ -585,7 +651,10 @@ class BaseObjectOrm:
|
|
|
585
651
|
@classmethod
|
|
586
652
|
def create(cls, timeout=None, files=None, *args, **kwargs):
|
|
587
653
|
base_url = cls.get_object_url()
|
|
588
|
-
|
|
654
|
+
operation = f"{cls.__name__}.create"
|
|
655
|
+
data = cls.serialize_for_json(
|
|
656
|
+
cls._with_sdk_owned_create_context(operation, dict(kwargs))
|
|
657
|
+
)
|
|
589
658
|
payload = {"json": data}
|
|
590
659
|
if files:
|
|
591
660
|
payload["files"] = files
|
|
@@ -604,6 +673,8 @@ class BaseObjectOrm:
|
|
|
604
673
|
@classmethod
|
|
605
674
|
def _destroy_by_reference(cls, public_reference, *args, timeout=None, **kwargs):
|
|
606
675
|
base_url = cls.get_object_url()
|
|
676
|
+
operation = f"{cls.__name__}.delete"
|
|
677
|
+
cls._reject_sdk_context_overrides(kwargs, operation=operation)
|
|
607
678
|
payload: dict[str, Any] = {}
|
|
608
679
|
|
|
609
680
|
if getattr(cls, "DESTROY_QUERY_PARAMS", None):
|
|
@@ -615,6 +686,10 @@ class BaseObjectOrm:
|
|
|
615
686
|
if data:
|
|
616
687
|
payload["json"] = data
|
|
617
688
|
|
|
689
|
+
query_context = cls._sdk_owned_query_context(operation)
|
|
690
|
+
if query_context:
|
|
691
|
+
payload.setdefault("params", {}).update(query_context)
|
|
692
|
+
|
|
618
693
|
r = make_request(
|
|
619
694
|
s=cls.build_session(),
|
|
620
695
|
loaders=cls.LOADERS,
|
|
@@ -634,8 +709,13 @@ class BaseObjectOrm:
|
|
|
634
709
|
def _patch_by_reference(cls, public_reference, *args, _into=None, **kwargs):
|
|
635
710
|
base_url = cls.get_object_url()
|
|
636
711
|
url = f"{base_url}/{public_reference}/"
|
|
712
|
+
operation = f"{cls.__name__}.patch"
|
|
713
|
+
cls._reject_sdk_context_overrides(kwargs, operation=operation)
|
|
637
714
|
data = cls.serialize_for_json(kwargs)
|
|
638
715
|
payload = {"json": data}
|
|
716
|
+
query_context = cls._sdk_owned_query_context(operation)
|
|
717
|
+
if query_context:
|
|
718
|
+
payload["params"] = query_context
|
|
639
719
|
|
|
640
720
|
r = make_request(
|
|
641
721
|
s=cls.build_session(),
|
|
@@ -769,7 +849,18 @@ class DetailActionObjectMixin:
|
|
|
769
849
|
expected_statuses: tuple[int, ...] = (200,),
|
|
770
850
|
empty_response: Any = None,
|
|
771
851
|
) -> Any:
|
|
772
|
-
payload = payload or {}
|
|
852
|
+
payload = dict(payload or {})
|
|
853
|
+
query_context = type(self)._sdk_owned_query_context(
|
|
854
|
+
f"{type(self).__name__}.{action_name}"
|
|
855
|
+
)
|
|
856
|
+
if query_context:
|
|
857
|
+
params = dict(payload.get("params") or {})
|
|
858
|
+
type(self)._reject_sdk_context_overrides(
|
|
859
|
+
params,
|
|
860
|
+
operation=f"{type(self).__name__}.{action_name}",
|
|
861
|
+
)
|
|
862
|
+
params.update(query_context)
|
|
863
|
+
payload["params"] = params
|
|
773
864
|
response = make_request(
|
|
774
865
|
s=type(self).build_session(),
|
|
775
866
|
loaders=type(self).LOADERS,
|
|
@@ -33,9 +33,16 @@ from mainsequence.project_context import (
|
|
|
33
33
|
get_project_runtime_context,
|
|
34
34
|
require_project_branch_context,
|
|
35
35
|
require_project_metatables_data_source,
|
|
36
|
+
resolve_project_environment_uid,
|
|
36
37
|
)
|
|
37
38
|
|
|
38
|
-
from ..base import
|
|
39
|
+
from ..base import (
|
|
40
|
+
BaseObjectOrm,
|
|
41
|
+
BasePydanticModel,
|
|
42
|
+
CurrentProjectEnvironmentResourceMixin,
|
|
43
|
+
LabelableObjectMixin,
|
|
44
|
+
ShareableObjectMixin,
|
|
45
|
+
)
|
|
39
46
|
from ..data_sources_interfaces import get_duckdb_interface_class, get_sqlite_interface_class
|
|
40
47
|
from ..dtype_codec import (
|
|
41
48
|
DATE,
|
|
@@ -1237,7 +1244,13 @@ class DataSource(BasePydanticModel, BaseObjectOrm):
|
|
|
1237
1244
|
raise NotImplementedError
|
|
1238
1245
|
|
|
1239
1246
|
|
|
1240
|
-
class MetaTable(
|
|
1247
|
+
class MetaTable(
|
|
1248
|
+
CurrentProjectEnvironmentResourceMixin,
|
|
1249
|
+
BasePydanticModel,
|
|
1250
|
+
LabelableObjectMixin,
|
|
1251
|
+
ShareableObjectMixin,
|
|
1252
|
+
BaseObjectOrm,
|
|
1253
|
+
):
|
|
1241
1254
|
ENDPOINT: ClassVar[str] = "meta-tables"
|
|
1242
1255
|
COLLECTION_CREATE_ROW_MODEL: ClassVar[type[ManagedMetaTableCollectionCreateRow]] = (
|
|
1243
1256
|
ManagedMetaTableCollectionCreateRow
|
|
@@ -1246,8 +1259,6 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1246
1259
|
"identifier": ["in", "exact", "contains"],
|
|
1247
1260
|
"uid": ["in", "exact"],
|
|
1248
1261
|
"data_source__uid": ["in", "exact"],
|
|
1249
|
-
"organization_project_environment__uid": ["in", "exact"],
|
|
1250
|
-
"organization_project_environment": ["isnull"],
|
|
1251
1262
|
"namespace": ["exact", "contains", "in", "isnull"],
|
|
1252
1263
|
"management_mode": ["exact", "in"],
|
|
1253
1264
|
"provisioning_status": ["exact", "in"],
|
|
@@ -1262,8 +1273,6 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1262
1273
|
"uid__in": "str",
|
|
1263
1274
|
"data_source__uid": "uid",
|
|
1264
1275
|
"data_source__uid__in": "uid",
|
|
1265
|
-
"organization_project_environment__uid": "uid",
|
|
1266
|
-
"organization_project_environment__uid__in": "uid",
|
|
1267
1276
|
"physical_schema": "str",
|
|
1268
1277
|
"physical_schema__in": "str",
|
|
1269
1278
|
"labels": "str",
|
|
@@ -1333,6 +1342,11 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1333
1342
|
|
|
1334
1343
|
model_config = ConfigDict(populate_by_name=True)
|
|
1335
1344
|
|
|
1345
|
+
@classmethod
|
|
1346
|
+
def _sdk_owned_create_context(cls, operation: str) -> dict[str, str]:
|
|
1347
|
+
# MetaTable collection creation has its own managed project_context contract.
|
|
1348
|
+
return {}
|
|
1349
|
+
|
|
1336
1350
|
@model_validator(mode="before")
|
|
1337
1351
|
@classmethod
|
|
1338
1352
|
def _strip_legacy_storage_hash(cls, value: Any) -> Any:
|
|
@@ -1426,9 +1440,11 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1426
1440
|
timeout: int | float | tuple[float, float] | None = None,
|
|
1427
1441
|
**filters: Any,
|
|
1428
1442
|
) -> list[MetaTable]:
|
|
1443
|
+
operation = f"{cls.__name__}.filter_by_body"
|
|
1444
|
+
cls._reject_sdk_context_overrides(filters, operation=operation)
|
|
1429
1445
|
response_json = cls._post_action(
|
|
1430
1446
|
"filter",
|
|
1431
|
-
filters,
|
|
1447
|
+
{**filters, **cls._sdk_owned_query_context(operation)},
|
|
1432
1448
|
timeout=timeout,
|
|
1433
1449
|
expected_statuses=(200,),
|
|
1434
1450
|
)
|
|
@@ -1473,7 +1489,12 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1473
1489
|
**kwargs,
|
|
1474
1490
|
):
|
|
1475
1491
|
url = f"{type(self).get_object_url().rstrip('/')}/{self._public_uid()}/"
|
|
1476
|
-
|
|
1492
|
+
operation = f"{type(self).__name__}.patch"
|
|
1493
|
+
type(self)._reject_sdk_context_overrides(kwargs, operation=operation)
|
|
1494
|
+
payload = {
|
|
1495
|
+
"json": serialize_to_json(kwargs),
|
|
1496
|
+
"params": type(self)._sdk_owned_query_context(operation),
|
|
1497
|
+
}
|
|
1477
1498
|
response = make_request(
|
|
1478
1499
|
s=type(self).build_session(),
|
|
1479
1500
|
loaders=type(self).LOADERS,
|
|
@@ -1505,6 +1526,7 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1505
1526
|
"full_delete_downstream_tables": full_delete_downstream_tables,
|
|
1506
1527
|
"delete_with_no_table": delete_with_no_table,
|
|
1507
1528
|
"override_protection": override_protection,
|
|
1529
|
+
**cls._sdk_owned_query_context(f"{cls.__name__}.delete"),
|
|
1508
1530
|
}
|
|
1509
1531
|
}
|
|
1510
1532
|
response = make_request(
|
|
@@ -1551,9 +1573,13 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1551
1573
|
raise ValueError(
|
|
1552
1574
|
"project_context is SDK-controlled and cannot be supplied by the caller."
|
|
1553
1575
|
)
|
|
1576
|
+
if "organization_project_environment_uid" in unvalidated_payload:
|
|
1577
|
+
raise ValueError(
|
|
1578
|
+
"organization_project_environment_uid is SDK-controlled and cannot "
|
|
1579
|
+
"be supplied by the caller."
|
|
1580
|
+
)
|
|
1554
1581
|
payload = MetaTableRegistrationRequest.model_validate(unvalidated_payload)
|
|
1555
|
-
|
|
1556
|
-
payload = _with_current_metatable_project_context(payload)
|
|
1582
|
+
payload = _with_current_metatable_project_context(payload)
|
|
1557
1583
|
response_json = cls._post_action(
|
|
1558
1584
|
"register",
|
|
1559
1585
|
payload,
|
|
@@ -1824,6 +1850,8 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1824
1850
|
q = (q or "").strip()
|
|
1825
1851
|
if not q:
|
|
1826
1852
|
raise ValueError("q is required")
|
|
1853
|
+
operation = f"{cls.__name__}.description_search"
|
|
1854
|
+
cls._reject_sdk_context_overrides(filters, operation=operation)
|
|
1827
1855
|
|
|
1828
1856
|
body = {
|
|
1829
1857
|
"q": q,
|
|
@@ -1839,6 +1867,7 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1839
1867
|
|
|
1840
1868
|
if filters:
|
|
1841
1869
|
body.update(filters)
|
|
1870
|
+
body.update(cls._sdk_owned_query_context(operation))
|
|
1842
1871
|
|
|
1843
1872
|
payload = {"params": serialize_to_json(body)}
|
|
1844
1873
|
response = make_request(
|
|
@@ -1858,8 +1887,14 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1858
1887
|
q = (q or "").strip()
|
|
1859
1888
|
if not q:
|
|
1860
1889
|
raise ValueError("q is required")
|
|
1890
|
+
operation = f"{cls.__name__}.column_search"
|
|
1891
|
+
cls._reject_sdk_context_overrides(filters, operation=operation)
|
|
1861
1892
|
|
|
1862
|
-
payload = {
|
|
1893
|
+
payload = {
|
|
1894
|
+
"params": serialize_to_json(
|
|
1895
|
+
{"q": q, **filters, **cls._sdk_owned_query_context(operation)}
|
|
1896
|
+
)
|
|
1897
|
+
}
|
|
1863
1898
|
response = make_request(
|
|
1864
1899
|
s=cls.build_session(),
|
|
1865
1900
|
loaders=cls.LOADERS,
|
|
@@ -3081,8 +3116,6 @@ class TimeIndexMetaTable(MetaTable):
|
|
|
3081
3116
|
"identifier": ["in", "exact", "contains"],
|
|
3082
3117
|
"uid": ["in", "exact"],
|
|
3083
3118
|
"data_source__uid": ["in", "exact"],
|
|
3084
|
-
"organization_project_environment__uid": ["in", "exact"],
|
|
3085
|
-
"organization_project_environment": ["isnull"],
|
|
3086
3119
|
"namespace": ["exact", "contains", "in", "isnull"],
|
|
3087
3120
|
"physical_schema": ["exact", "in"],
|
|
3088
3121
|
"physical_table_name": ["exact", "contains", "in"],
|
|
@@ -3093,8 +3126,6 @@ class TimeIndexMetaTable(MetaTable):
|
|
|
3093
3126
|
"uid__in": "uid",
|
|
3094
3127
|
"data_source__uid": "uid",
|
|
3095
3128
|
"data_source__uid__in": "uid",
|
|
3096
|
-
"organization_project_environment__uid": "uid",
|
|
3097
|
-
"organization_project_environment__uid__in": "uid",
|
|
3098
3129
|
"physical_schema": "str",
|
|
3099
3130
|
"physical_schema__in": "str",
|
|
3100
3131
|
"labels": "str",
|
|
@@ -3333,6 +3364,11 @@ class TimeIndexMetaTable(MetaTable):
|
|
|
3333
3364
|
raise ValueError(
|
|
3334
3365
|
"project_context is SDK-controlled and cannot be supplied by the caller."
|
|
3335
3366
|
)
|
|
3367
|
+
if "organization_project_environment_uid" in unvalidated_payload:
|
|
3368
|
+
raise ValueError(
|
|
3369
|
+
"organization_project_environment_uid is SDK-controlled and cannot "
|
|
3370
|
+
"be supplied by the caller."
|
|
3371
|
+
)
|
|
3336
3372
|
payload = TimeIndexMetaTableRegistrationRequest.model_validate(unvalidated_payload)
|
|
3337
3373
|
payload_json = _with_current_metatable_project_context(payload)
|
|
3338
3374
|
request_payload = {"json": serialize_to_json(payload_json)}
|
|
@@ -4708,6 +4744,10 @@ def _current_metatable_project_context() -> MetaTableProjectContextRequest:
|
|
|
4708
4744
|
return _metatable_project_context_from_resolution(get_project_runtime_context())
|
|
4709
4745
|
|
|
4710
4746
|
|
|
4747
|
+
def _current_metatable_environment_uid() -> str:
|
|
4748
|
+
return resolve_project_environment_uid("External MetaTable registration")
|
|
4749
|
+
|
|
4750
|
+
|
|
4711
4751
|
def _metatable_project_context_for_request() -> MetaTableProjectContextRequest | None:
|
|
4712
4752
|
return _current_metatable_project_context()
|
|
4713
4753
|
|
|
@@ -4718,7 +4758,15 @@ def _with_current_metatable_project_context(
|
|
|
4718
4758
|
payload_json = _payload_json(payload)
|
|
4719
4759
|
if "project_context" in payload_json:
|
|
4720
4760
|
raise ValueError("project_context is SDK-controlled and cannot be supplied by the caller.")
|
|
4761
|
+
if "organization_project_environment_uid" in payload_json:
|
|
4762
|
+
raise ValueError(
|
|
4763
|
+
"organization_project_environment_uid is SDK-controlled and cannot "
|
|
4764
|
+
"be supplied by the caller."
|
|
4765
|
+
)
|
|
4721
4766
|
if payload_json.get("management_mode") == "external_registered":
|
|
4767
|
+
payload_json["organization_project_environment_uid"] = (
|
|
4768
|
+
_current_metatable_environment_uid()
|
|
4769
|
+
)
|
|
4722
4770
|
return payload_json
|
|
4723
4771
|
project_context = _metatable_project_context_for_request()
|
|
4724
4772
|
if project_context is not None:
|
|
@@ -4732,13 +4780,23 @@ def _with_current_metatable_project_context_collection(
|
|
|
4732
4780
|
payload_rows = [dict(payload) for payload in payloads]
|
|
4733
4781
|
if any("project_context" in row for row in payload_rows):
|
|
4734
4782
|
raise ValueError("project_context is SDK-controlled and cannot be supplied by the caller.")
|
|
4783
|
+
if any("organization_project_environment_uid" in row for row in payload_rows):
|
|
4784
|
+
raise ValueError(
|
|
4785
|
+
"organization_project_environment_uid is SDK-controlled and cannot "
|
|
4786
|
+
"be supplied by the caller."
|
|
4787
|
+
)
|
|
4735
4788
|
managed_rows = [
|
|
4736
4789
|
row for row in payload_rows if row.get("management_mode") != "external_registered"
|
|
4737
4790
|
]
|
|
4738
|
-
|
|
4739
|
-
|
|
4791
|
+
external_rows = [
|
|
4792
|
+
row for row in payload_rows if row.get("management_mode") == "external_registered"
|
|
4793
|
+
]
|
|
4794
|
+
if external_rows:
|
|
4795
|
+
environment_uid = _current_metatable_environment_uid()
|
|
4796
|
+
for row in external_rows:
|
|
4797
|
+
row["organization_project_environment_uid"] = environment_uid
|
|
4740
4798
|
|
|
4741
|
-
project_context = _metatable_project_context_for_request()
|
|
4799
|
+
project_context = _metatable_project_context_for_request() if managed_rows else None
|
|
4742
4800
|
if project_context is not None:
|
|
4743
4801
|
current_project_context = project_context.model_dump(mode="json")
|
|
4744
4802
|
for row in managed_rows:
|
|
@@ -17,6 +17,7 @@ from .base import (
|
|
|
17
17
|
BaseObjectOrm,
|
|
18
18
|
BasePydanticModel,
|
|
19
19
|
CurrentProjectBranchCollectionMixin,
|
|
20
|
+
CurrentProjectEnvironmentResourceMixin,
|
|
20
21
|
LabelableObjectMixin,
|
|
21
22
|
ShareableObjectMixin,
|
|
22
23
|
)
|
|
@@ -899,7 +900,12 @@ class DynamicResource(BasePydanticModel, BaseObjectOrm):
|
|
|
899
900
|
return super().create(*args, **kwargs)
|
|
900
901
|
|
|
901
902
|
|
|
902
|
-
class Bucket(
|
|
903
|
+
class Bucket(
|
|
904
|
+
CurrentProjectEnvironmentResourceMixin,
|
|
905
|
+
ShareableObjectMixin,
|
|
906
|
+
BasePydanticModel,
|
|
907
|
+
BaseObjectOrm,
|
|
908
|
+
):
|
|
903
909
|
FILTERSET_FIELDS: ClassVar[dict[str, list[str]]] = {
|
|
904
910
|
"uid": ["in", "exact"],
|
|
905
911
|
"name": ["in", "exact"],
|
|
@@ -924,9 +930,34 @@ class Bucket(ShareableObjectMixin, BasePydanticModel, BaseObjectOrm):
|
|
|
924
930
|
examples=["daily_positions_report.pdf"],
|
|
925
931
|
json_schema_extra={"label": "Bucket Name"},
|
|
926
932
|
)
|
|
933
|
+
organization_project_environment_uid: str | None = Field(
|
|
934
|
+
None,
|
|
935
|
+
description="Read-only UID of the owning Organization Environment.",
|
|
936
|
+
)
|
|
937
|
+
organization_project_environment_name: str | None = Field(
|
|
938
|
+
None,
|
|
939
|
+
description="Read-only name of the owning Organization Environment.",
|
|
940
|
+
)
|
|
941
|
+
physical_bucket_uid: str | None = Field(
|
|
942
|
+
None,
|
|
943
|
+
description="Read-only UID of the provider-backed physical bucket.",
|
|
944
|
+
)
|
|
945
|
+
backing_ready: bool | None = Field(
|
|
946
|
+
None,
|
|
947
|
+
description="Whether the physical bucket backing is ready for use.",
|
|
948
|
+
)
|
|
949
|
+
backing_last_discovered_at: datetime.datetime | None = Field(
|
|
950
|
+
None,
|
|
951
|
+
description="Last successful physical backing discovery timestamp.",
|
|
952
|
+
)
|
|
927
953
|
|
|
928
954
|
|
|
929
|
-
class Artifact(
|
|
955
|
+
class Artifact(
|
|
956
|
+
CurrentProjectEnvironmentResourceMixin,
|
|
957
|
+
ShareableObjectMixin,
|
|
958
|
+
BasePydanticModel,
|
|
959
|
+
BaseObjectOrm,
|
|
960
|
+
):
|
|
930
961
|
uid: str | None = Field(
|
|
931
962
|
None,
|
|
932
963
|
title="Artifact UID",
|
|
@@ -970,6 +1001,14 @@ class Artifact(ShareableObjectMixin, BasePydanticModel, BaseObjectOrm):
|
|
|
970
1001
|
examples=["2026-03-15T10:30:00Z"],
|
|
971
1002
|
json_schema_extra={"label": "Creation Date"},
|
|
972
1003
|
)
|
|
1004
|
+
organization_project_environment_uid: str | None = Field(
|
|
1005
|
+
None,
|
|
1006
|
+
description="Read-only UID of the owning Organization Environment.",
|
|
1007
|
+
)
|
|
1008
|
+
organization_project_environment_name: str | None = Field(
|
|
1009
|
+
None,
|
|
1010
|
+
description="Read-only name of the owning Organization Environment.",
|
|
1011
|
+
)
|
|
973
1012
|
|
|
974
1013
|
@classmethod
|
|
975
1014
|
def upload_file(cls, filepath, name, bucket_name=None):
|
|
@@ -985,10 +1024,13 @@ class Artifact(ShareableObjectMixin, BasePydanticModel, BaseObjectOrm):
|
|
|
985
1024
|
url = cls.get_object_url() + "/get-or-create/"
|
|
986
1025
|
s = cls.build_session()
|
|
987
1026
|
with open(filepath, "rb") as f:
|
|
988
|
-
data =
|
|
989
|
-
"
|
|
990
|
-
|
|
991
|
-
|
|
1027
|
+
data = cls._with_sdk_owned_create_context(
|
|
1028
|
+
"Artifact.get_or_create",
|
|
1029
|
+
{
|
|
1030
|
+
"name": name,
|
|
1031
|
+
"bucket_name": bucket_name if bucket_name else "default_bucket",
|
|
1032
|
+
},
|
|
1033
|
+
)
|
|
992
1034
|
files = {"content": (str(filepath), f, "application/pdf")}
|
|
993
1035
|
payload = {"json": data, "files": files}
|
|
994
1036
|
r = make_request(s=s, loaders=cls.LOADERS, r_type="POST", url=url, payload=payload)
|
|
@@ -1029,7 +1071,12 @@ def _norm_kwargs(kwargs: dict[str, Any]) -> tuple[tuple[str, Any], ...]:
|
|
|
1029
1071
|
return tuple(sorted(items))
|
|
1030
1072
|
|
|
1031
1073
|
|
|
1032
|
-
class Secret(
|
|
1074
|
+
class Secret(
|
|
1075
|
+
CurrentProjectEnvironmentResourceMixin,
|
|
1076
|
+
ShareableObjectMixin,
|
|
1077
|
+
BasePydanticModel,
|
|
1078
|
+
BaseObjectOrm,
|
|
1079
|
+
):
|
|
1033
1080
|
FILTERSET_FIELDS: ClassVar[dict[str, list[str]]] = {
|
|
1034
1081
|
"uid": ["in", "exact"],
|
|
1035
1082
|
"name": ["in", "exact"],
|
|
@@ -1047,6 +1094,14 @@ class Secret(ShareableObjectMixin, BasePydanticModel, BaseObjectOrm):
|
|
|
1047
1094
|
description="Secret value. The create endpoint may omit it in the response.",
|
|
1048
1095
|
exclude=True,
|
|
1049
1096
|
)
|
|
1097
|
+
organization_project_environment_uid: str | None = Field(
|
|
1098
|
+
None,
|
|
1099
|
+
description="Read-only UID of the owning Organization Environment.",
|
|
1100
|
+
)
|
|
1101
|
+
organization_project_environment_name: str | None = Field(
|
|
1102
|
+
None,
|
|
1103
|
+
description="Read-only name of the owning Organization Environment.",
|
|
1104
|
+
)
|
|
1050
1105
|
|
|
1051
1106
|
@classmethod
|
|
1052
1107
|
def create(
|
|
@@ -1073,7 +1128,12 @@ class Secret(ShareableObjectMixin, BasePydanticModel, BaseObjectOrm):
|
|
|
1073
1128
|
return super().create(name=name, value=value, timeout=timeout)
|
|
1074
1129
|
|
|
1075
1130
|
|
|
1076
|
-
class Constant(
|
|
1131
|
+
class Constant(
|
|
1132
|
+
CurrentProjectEnvironmentResourceMixin,
|
|
1133
|
+
ShareableObjectMixin,
|
|
1134
|
+
BasePydanticModel,
|
|
1135
|
+
BaseObjectOrm,
|
|
1136
|
+
):
|
|
1077
1137
|
"""
|
|
1078
1138
|
Simple organization-level constant.
|
|
1079
1139
|
"""
|
|
@@ -1100,6 +1160,14 @@ class Constant(ShareableObjectMixin, BasePydanticModel, BaseObjectOrm):
|
|
|
1100
1160
|
description="Small JSON value (string/number/bool/object/array). Keep it small (e.g., <=10KB).",
|
|
1101
1161
|
)
|
|
1102
1162
|
category: str | None = None
|
|
1163
|
+
organization_project_environment_uid: str | None = Field(
|
|
1164
|
+
None,
|
|
1165
|
+
description="Read-only UID of the owning Organization Environment.",
|
|
1166
|
+
)
|
|
1167
|
+
organization_project_environment_name: str | None = Field(
|
|
1168
|
+
None,
|
|
1169
|
+
description="Read-only name of the owning Organization Environment.",
|
|
1170
|
+
)
|
|
1103
1171
|
|
|
1104
1172
|
# Class-level cache & lock (Pydantic ignores ClassVar)
|
|
1105
1173
|
_filter_cache: ClassVar[TTLCache] = TTLCache(maxsize=512, ttl=600)
|
|
@@ -168,6 +168,15 @@ class Job(CurrentProjectBranchCollectionMixin, BaseObjectOrm, BasePydanticModel)
|
|
|
168
168
|
examples=["5a28020a-0f1b-47ee-aab8-334286234bea"],
|
|
169
169
|
)
|
|
170
170
|
|
|
171
|
+
organization_project_environment_uid: str | None = Field(
|
|
172
|
+
default=None,
|
|
173
|
+
description=(
|
|
174
|
+
"Read-only Organization Environment UID derived by the backend "
|
|
175
|
+
"from the owning ProjectBranch."
|
|
176
|
+
),
|
|
177
|
+
examples=["58218213-5e4e-43de-a5bd-6757f4e1c8f6"],
|
|
178
|
+
)
|
|
179
|
+
|
|
171
180
|
project_repo_hash: str | None = Field(
|
|
172
181
|
default=None,
|
|
173
182
|
description=(
|
|
@@ -28,6 +28,10 @@ class ProjectBranchContextRequiredError(ProjectRuntimeContextError):
|
|
|
28
28
|
"""Raised when an operation requires a registered current ProjectBranch."""
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class ProjectEnvironmentContextRequiredError(ProjectBranchContextRequiredError):
|
|
32
|
+
"""Raised when the current ProjectBranch has no resolved Environment."""
|
|
33
|
+
|
|
34
|
+
|
|
31
35
|
class ProjectDataSourceContextRequiredError(ProjectRuntimeContextError):
|
|
32
36
|
"""Raised when project-derived data access has no usable branch DataSource."""
|
|
33
37
|
|
|
@@ -406,6 +410,19 @@ def resolve_project_branch_uid(operation: str, supplied_uid: Any = None) -> str:
|
|
|
406
410
|
return resolved_uid
|
|
407
411
|
|
|
408
412
|
|
|
413
|
+
def resolve_project_environment_uid(operation: str) -> str:
|
|
414
|
+
"""Return the Environment UID derived from the process-frozen ProjectBranch."""
|
|
415
|
+
|
|
416
|
+
context = require_project_branch_context(operation)
|
|
417
|
+
environment_uid = str(context.organization_project_environment_uid or "").strip()
|
|
418
|
+
if not environment_uid:
|
|
419
|
+
raise ProjectEnvironmentContextRequiredError(
|
|
420
|
+
f"{operation} requires an Organization Environment resolved from "
|
|
421
|
+
f"ProjectBranch {context.project_branch_uid!r}, but none was returned."
|
|
422
|
+
)
|
|
423
|
+
return environment_uid
|
|
424
|
+
|
|
425
|
+
|
|
409
426
|
def scope_current_project_branch_filters(
|
|
410
427
|
operation: str,
|
|
411
428
|
filters: Mapping[str, Any],
|
|
@@ -472,6 +489,7 @@ __all__ = [
|
|
|
472
489
|
"GitProjectSourceContext",
|
|
473
490
|
"ProjectBranchContextRequiredError",
|
|
474
491
|
"ProjectDataSourceContextRequiredError",
|
|
492
|
+
"ProjectEnvironmentContextRequiredError",
|
|
475
493
|
"ProjectRuntimeContext",
|
|
476
494
|
"ProjectRuntimeContextError",
|
|
477
495
|
"ProjectSourceContextDriftError",
|
|
@@ -479,6 +497,7 @@ __all__ = [
|
|
|
479
497
|
"normalize_git_repository_identity",
|
|
480
498
|
"require_project_branch_context",
|
|
481
499
|
"require_project_metatables_data_source",
|
|
500
|
+
"resolve_project_environment_uid",
|
|
482
501
|
"resolve_project_branch_uid",
|
|
483
502
|
"scope_current_project_branch_filters",
|
|
484
503
|
"validate_project_source_context",
|